<p>首先,为什么要提出<code>StopIteration</code>。您对<code>preorder_traversal</code>的定义从以下开始:</p>
<pre><code>try:
yield obj
except IgnoreSubtree:
return
</code></pre>
<p>在生成器中,<code>return</code>语句<em>等价于<code>raise StopIteration</code></em>。在python3.3+中,您实际上可以使用<code>return value</code>,它相当于<code>raise StopIteration(value)</code>。在</p>
<p>因此,您正在某个异常中<code>throw</code>,它被执行<code>return</code>的生成器捕获,从而引发一个<code>StopIteration</code>。无论何时调用<code>send</code>,<code>next</code>或<code>throw</code>如果生成器在没有找到<code>yield</code>的情况下结束其执行,则可能会引发<code>next</code>或{<cd1>},因此每当跳过子树将结束迭代时,您在测试中使用的代码注定会引发一个<code>StopIteration</code>。在</p>
<p>换句话说,您的测试是有缺陷的,因为<code>throw</code>调用可以引发异常,即使您有正确的生成器实现。因此,您应该在<code>try</code>语句中包装该调用:</p>
^{pr2}$
<p>或者,您可以使用<a href="https://docs.python.org/3/library/contextlib.html#contextlib.suppress" rel="nofollow">^{<cd18>}</a>上下文管理器来取消<code>StopIteration</code>:</p>
<pre><code>with suppress(StopIteration):
for o in iterator:
...
iterator.throw(IgnoreSubtree)
</code></pre>
<p>如果您不使用python3.4,那么可以使用<a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" rel="nofollow">^{<cd20>}</a>修饰符轻松地重新实现这个上下文管理器(从python2.6开始就可以使用它):</p>
<pre><code>def suppress(*exceptions):
try:
yield
except exceptions:
pass
</code></pre>
<p>你的代码基本上是正确的。如果您使用的是python3.3+,可以将其简化为:</p>
<pre><code>def preorder_traversal(obj):
try:
yield obj
except IgnoreSubtree:
return
else:
if isinstance(obj, dict):
for k, v in obj.items():
yield from preorder_traversal(v)
</code></pre>
<p>一旦外部的<code>StopIteration</code>被抑制,<code>throw</code>,您的实现不会对我产生任何错误。结果也是你所期望的。
不幸的是,如果没有<code>yield from</code>,我看不到任何简化控制流的方法。在</p>