从Python 3.3开始,如果生成器函数返回一个值,那么它将成为引发的StopIteration异常的值.这可以通过多种方式收集:
yield from
表达式的值,暗示封闭函数也是一个生成器.
将调用包含在try/except块中next()
或.send()
中.
但是,如果我只是想在for循环中迭代生成器 - 最简单的方法 - 似乎没有办法收集StopIteration异常的值,从而收集返回值.我使用一个简单的例子,其中生成器产生值,并在最后返回某种摘要(运行总计,平均值,时间统计等).
for i in produce_values(): do_something(i) values_summary = ....??
一种方法是自己处理循环:
values_iter = produce_values() try: while True: i = next(values_iter) do_something(i) except StopIteration as e: values_summary = e.value
但这会抛弃for循环的简单性.我无法使用,yield from
因为这需要调用代码本身就是一个生成器.有没有比上面显示的roll-one-own for循环更简单的方法?
结合@Chad S.和@KT的答案,最简单的似乎是使用迭代器协议将我的生成器函数转换为类:
class ValueGenerator(): def __iter__(self): yield 1 yield 2 # and so on self.summary = {...} vg = ValueGenerator() for i in vg: do_something(i) values_summary = vg.summary
如果我不能重构价值生产者,@ Ferdinand Beyer的答案是最简单的.
您可以value
将StopIteration
(并且可以说是StopIteration
本身)的属性视为实现细节,而不是设计用于"普通"代码.
看看PEP 380,它指定了yield from
Python 3.3 的特性:它讨论了一些StopIteration
用于携带返回值的替代方案.
由于您不应该在普通for
循环中获取返回值,因此没有语法.你不应该StopIteration
明确地捕捉到同样的方式.
对你的情况一个很好的解决方案是一个小的实用程序类(可能对标准库有用):
class Generator: def __init__(self, gen): self.gen = gen def __iter__(self): self.value = yield from self.gen
这将包装任何生成器并捕获其返回值以便稍后检查:
>>> def test(): ... yield 1 ... return 2 ... >>> gen = Generator(test()) >>> for i in gen: ... print(i) ... 1 >>> print(gen.value) 2
您可以创建一个帮助程序包装器,它将捕获StopIteration
并为您提取值:
from functools import wraps class ValueKeepingGenerator(object): def __init__(self, g): self.g = g self.value = None def __iter__(self): self.value = yield from self.g def keep_value(f): @wraps(f) def g(*args, **kwargs): return ValueKeepingGenerator(f(*args, **kwargs)) return g @keep_value def f(): yield 1 yield 2 return "Hi" v = f() for x in v: print(x) print(v.value)