I like turkus https://stackoverflow.com/a/39427083答案,但是显示的示例大多是理论性的,在日常编码中并不常见。
生成器函数(与yield
) 和返回生成器的函数是生成器函数是延迟计算的.
考虑本次会议:
$ python
Python 3.6.0
[GCC 6.3.1 20170109] on linux
>>> def a():
... print('in a')
... yield 0
...
>>> def b():
... print('in b')
... return iter(range(1))
...
>>> aa = a() # Lazy evaluation - nothing printed after this line.
>>> next(aa)
in a
0
>>> next(aa)
Traceback ...
StopIteration
>>> bb = b() # Eager evaluation - whole function is executed after this.
in b
>>> next(bb)
0
>>> next(bb)
Traceback ...
StopIteration
它们都不是金子弹。
为了给您提供一个真实的示例,说明这种惰性求值在您的代码中产生巨大差异,请查看此示例。
def get_numbers(x: int):
if x < 0:
raise ValueError("Value cannot be negative")
for i in range(x):
yield i
try:
numbers = get_numbers(-5)
except ValueError:
pass # log or something
else:
print(list(numbers)) # <== ValueError is thrown here!
这就是惰性求值实际上对你的函数不利的地方。它会在可能错误的地方抛出异常,因为其目的是让它在开始时失败,而不是在迭代期间失败。
通过此实现,您将触发生成器函数和管理异常的责任传递给用户,这是乏味且有点丑陋的:
import itertools
try:
numbers = get_numbers(-5)
first = next(numbers)
numbers = itertools.chain([first], numbers)
except ValueError:
...
解决这个问题的最好方法是创建一个返回生成器而不是生成器函数的函数:
def get_numbers(x: int):
if x < 0:
raise ValueError("Value cannot be negative")
return (i for i in range(x)) # I know it can be just `return range(x)`, but I keep it that way to make a point.
正如您所看到的,没有“最佳方法”来做到这一点,这两种选择都是可行的。这完全取决于您希望事情如何运作。