<p>正如audiodude所提到的,您的<code>iterator.throw(IgnoreSubtree)</code>返回<code>iterator</code>的下一个值(暂时忽略了复杂的异常处理),因此它正在消耗您期望在<code>test_ignore_subtree</code>中的下一次循环的<code>2</code>附加到<code>out</code>。在</p>
<p>您还询问了抛出<code>StopIteration</code>的原因;<code>Exception</code>被抛出/捕获的顺序是:</p>
<ul>
<li><code>iterator.throw(IgnoreSubtree)</code>抛出一个在<code>preorder_traversal</code>的内循环中捕获的<code>IgnoreSubtree</code></li>
<li>使用<code>IgnoreSubtree</code>将{<cd9>}路由到内部迭代器中</li>
<li><code>IgnoreSubtree</code>在<code>except IgnoreSubtree:</code>处被捕获,并调用<code>return</code>;但是,<code>iterator.throw(e)</code>希望从内部迭代器中获得下一个值,该迭代器刚刚进行了<code>return</code>,因此引发了一个<code>StopIteration</code>。在</li>
<li>在原始的<code>iterator.throw(IgnoreSubtree)</code>返回之前,它将再次通过<code>preorder_traversal</code>中的外部循环,因为它希望从<code>iterator</code>返回下一个值。在</li>
</ul>
<p>我希望这有帮助!在</p>
<p><strong>更新</strong></p>
<p>以下是我将使用的基本方案的实现,以及通过nosetest:</p>
<pre><code>from nose.tools import eq_
def preorder_traversal(obj, ignore_only_descendents_of=None, ignore_subtrees=None):
if ignore_subtrees and obj in ignore_subtrees:
return
yield obj
if ignore_only_descendents_of and obj in ignore_only_descendents_of:
return
if isinstance(obj, dict):
for k, v in iter(sorted(obj.iteritems())):
iterator = preorder_traversal(v, ignore_only_descendents_of, ignore_subtrees)
for o in iterator:
yield o
def test_ignore_subtree():
obj = {'a': {'c': 3}, 'b': 2, 'd': {'e': {'f': 4}}, 'g': 5, 'h': 6}
ignore_only_descendents_of = [{'e': {'f': 4}}]
ignore_subtrees = [{'c': 3}, 5]
iterator = preorder_traversal(obj, ignore_only_descendents_of, ignore_subtrees)
out = []
for o in iterator:
out.append(o)
expected = [obj, 2, {'e': {'f': 4}}, 6]
eq_(expected, out)
</code></pre>
<p>注意事项:</p>
<ul>
<li>您的示例允许排除<code>{'c':3}</code>的子代,同时包含<code>{'c':3}</code>本身;我发现这有点令人困惑,因为我希望您通常希望排除包括其根的整个子树,所以我将<code>preorder_traversal</code>更改为以每种方式排除两个可选的内容列表。在</li>
<li>将子树移到迭代器本身看起来更简洁;您可以避免使用<code>Exception</code>s来完全控制流。在</li>
<li>更复杂的例子演示了两种类型的子树排除。在</li>
</ul>