生成器和返回生成器的函数之间的区别

2024-01-14

我正在用生成器调试一些代码并提出这个问题。假设我有一个生成器函数

def f(x):
    yield x

和一个返回生成器的函数:

def g(x):
    return f(x)

他们肯定会返回相同的东西。在 Python 代码中互换使用它们时会有什么区别吗?有什么方法可以区分两者(无需inspect)?


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.

正如您所看到的,没有“最佳方法”来做到这一点,这两种选择都是可行的。这完全取决于您希望事情如何运作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生成器和返回生成器的函数之间的区别 的相关文章

随机推荐