[英]Why can't my except block catch a StopIteration exception?
我想用这个程序做的是我希望它通过使用递归一遍又一遍地返回“是”和“否”。 请帮忙? 难道我做错了什么?
def yes_or_no():
for x in ("yes","no"):
try:
yield x
except (StopIteration):
return yes_or_no()
gen = yes_or_no()
print(next(gen))
print(next(gen))
print(next(gen))
一旦它到达第三次打印,它就会说 StopIteration 即使我认为我在错误处理中发现了它?
在yield
调用中不会引发StopIteration
异常,因此您不会使用该 try/except 设置来捕获它。
通过在您的 function 中有一个yield
语句,您已经将它变成了一个生成器(您似乎理解)。 每次评估生成器时,它都会在上次完成的产量处重新进入,并继续进行直到下一次产量或 function 完成。 因此,在您的第三次next()
中,执行将在yield
处恢复,返回for
循环,看到它已完成,然后继续执行,在这种情况下,这只是 function 的结束。 function 将返回,因此生成器将提高其 StopIteration。
我不建议您为此任务使用递归; 只需在 ("yes, "no") 循环周围使用外部无限循环(或者更好的是,来自 itertools 的东西)。
如果您真的想使用递归,那么您可能想尝试
def yes_or_no():
for x in ("yes", "no"):
yield x
yield from yes_or_no()
我认为yield from
需要 Python >= 3.3。
循环内的yield x
永远不会raise StopIteration
,因此您无法捕获它。 Python for
循环是使用StopIteration
实现的,是的,但是在您处理它之前就捕获了那里引发的实例。
使您想到的工作的方法是……让循环运行,然后产生元素的 rest。 递归:
def yes_or_no():
for x in ("yes","no"):
yield x
yield from yes_or_no()
顺便说一句: yield from
是您继续产生递归调用产生的元素所需要的。 生成器的return
值并不能帮助您生成更多元素。 具有讽刺意味的是,事实上,它设置了导致生成器耗尽元素的异常:
>>> def example():
... yield 1
... return 2
...
>>> x = example()
>>> next(x)
1
>>> next(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: 2
(当然,您的整个目标是yes_or_no()
不会用完元素,所以。)
无论如何,为此使用递归是一个坏主意,因为您人为地限制了它可以工作的时间(直到您用完堆栈帧)。 只需迭代:
def yes_or_no():
while True:
yield 'yes'
yield 'no'
或使用itertools
:
>>> import itertools
>>> x = itertools.cycle(('yes', 'no'))
>>> next(x)
'yes'
>>> next(x)
'no'
>>> next(x)
'yes'
>>> next(x)
'no'
那么如何在用户代码中充分利用StopIteration
呢? 通过在迭代器实现中显式提升它(尽管最明显的方法已经为您完成了),或者在您使用next
手动推进迭代器时捕获它。 例子:
class SingletonIterator:
"""Proxy iterator to allow 'iteration' over a fictitious sequence
holding a single element, the `value`."""
def __init__(self, value):
self._value = value
self._yielded = False
def __iter__(self):
return self
def __next__(self):
if self._yielded:
raise StopIteration
self._yielded = True
return self._value
def first(predicate, sequence):
"""The first element `e` of `sequence` satisfying `predicate(e)`.
Raises ValueError for an empty sequence."""
try:
return next(e for e in sequence if predicate(e))
except StopIteration:
raise ValueError('empty sequence')
这是一个简单的递归解决方案:
def yes_or_no():
yield from ('yes', 'no')
yield from yes_or_no()
编辑:
itertools
模块具有 function cycle
,该循环接收一个迭代器并返回一个循环通过其输入的生成器。
import itertools
def yes_or_no():
yield from itertools.cycle(("yes", "no"))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.