生成器中的 return 和 return None:PEP 指南

return and return None in a generator: PEP guidelines

根据 PEP 8,我们应该在函数声明中保持一致,并确保它们都具有相同的 return 模式,即所有都应该 return 一个表达式,或者都不应该。但是,我不确定如何将其应用于生成器。

生成器将 yield 值,只要代码到达它们,除非遇到 return 语句,在这种情况下它将停止迭代。但是,我没有看到任何 returning 来自生成器函数的值的用例。本着这种精神,我不明白为什么用明确的 return None 结束这样的函数 - 从 PEP 8 的角度来看 - 是有用的。换句话说,如果 return 表达式仅在 yield'ing 结束时才达到,为什么我们应该用语言表达生成器的 return 语句?

示例:在下面的代码中,我没有看到如何使用 hello()100 分配给变量(因此使用 return 语句)。那么,为什么 PEP 8 期望我们写一个 return 语句(无论是 100 还是 None)。

def hello():
    for i in range(5):
      yield i

    return 100


h = [x for x in hello()]
g = hello()

print(h)    
# [0, 1, 2, 3, 4]
print(g)
# <generator object hello at 0x7fd2f285a7d8>
# can we ever get 100?

你误读了PEP8。 PEP8 声明:

Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should.

(大胆强调我的)

您应该在单个函数中使用 return 的方式保持一致,而不是在整个项目中使用。

使用return,它是函数中唯一的return语句。

However, I don't see any use-case in which returning a value from a generator function can happen.

生成器的 return 值附加到引发的 StopIteration 异常:

>>> def gen():
...     if False: yield
...     return 'Return value'
...
>>> try:
...     next(gen())
... except StopIteration as ex:
...     print(ex.value)
...
Return value

这也是yield from产生价值的机制; yield from 的 return 值是 StopIteration 异常的 value 属性。因此,生成器可以 return 通过使用 return result:

使用 result = yield from generator 编码的结果
>>> def bar():
...     result = yield from gen()
...     print('gen() returned', result)
...
>>> next(bar(), None)
gen() returned Return value

此功能在Python标准库中使用;例如在 asyncio 库中,StopIteration 的值用于传递 Task 结果,而 @coroutine 装饰器使用 res = yield from ... 到 运行 包装generator 或 awaitable 并传递 return 值。

所以,从 PEP-8 的角度来看,对于生成器,有两种可能性:

  • 您正在使用 return 提前退出生成器,比如在 if 循环中。使用return,无需添加None:

    def foo():
        while bar:
            yield ham
            if spam:
                return
    
  • 您正在使用 return <something> 退出 并且 设置 StopIteration.value。在整个生成器中始终如一地使用 return <something> 即使 returning None:

    def foo():
        for bar in baz:
            yield bar
            if spam:
                return 'The bar bazzed the spam'
        return None