【问题标题】:Return in generator together with yield in Python 3.3在 Python 3.3 中与生成器一起返回
【发布时间】:2021-03-22 19:07:20
【问题描述】:

在 Python 2 中,函数定义中 return 与 yield 一起出现错误。但是对于 Python 3.3 中的这段代码

def f():
  return 3
  yield 2

x = f()
print(x.__next__())

在带有yield的函数中使用return没有错误。但是,当调用函数__next__ 时,会抛出异常 StopIteration。为什么不只是返回值3?这个返回值是否被忽略了?

【问题讨论】:

    标签: python python-3.x generator


    【解决方案1】:

    这是 Python 3.3 中的一项新功能(正如注释所述,它甚至在 3.2 中都不起作用)。很像生成器中的return 长期以来等价于raise StopIteration(),生成器中的return <something> 现在等价于raise StopIteration(<something>)。因此,您看到的异常应该打印为StopIteration: 3,并且可以通过异常对象上的属性value 访问该值。如果生成器被委派使用(也是新的)yield from 语法,那么它就是结果。详情请见PEP 380

    def f():
        return 1
        yield 2
    
    def g():
        x = yield from f()
        print(x)
    
    # g is still a generator so we need to iterate to run it:
    for _ in g():
        pass
    

    这会打印1,但不会打印2

    【讨论】:

      【解决方案2】:

      返回值不会被忽略,但生成器只yield 值,return 只是结束生成器,在这种情况下提前结束。在这种情况下,推进生成器永远不会到达yield 语句。

      每当迭代器到达要产生的值的“结束”时,StopIteration 必须被提升。发电机也不例外。然而,从 Python 3.3 开始,任何 return 表达式都将成为异常的值:

      >>> def gen():
      ...     return 3
      ...     yield 2
      ... 
      >>> try:
      ...     next(gen())
      ... except StopIteration as ex:
      ...     e = ex
      ... 
      >>> e
      StopIteration(3,)
      >>> e.value
      3
      

      使用next()函数推进迭代器,而不是直接调用.__next__()

      print(next(x))
      

      【讨论】:

      • return 的值不会被忽略,它要么是语法错误(在 3.2 及更早版本中),要么是未被忽略(在 3.3 及更高版本中)。
      • @delnan:该值未使用;您不能使用return 代替yield。函数结束,所以StopIteration 被提升,返回值最多被丢弃。 OP 知道在 3.3 之前返回值是语法错误。
      • return 确实不能替代yield,但值不是是徒劳的。它可以作为异常对象的属性使用,yield from 可以方便地访问它。
      • @delnan:对,刚刚找到它:引发的 StopIteration 实例的 value 属性 [...] 可以显式设置 [...] 或通过从子生成器;
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-16
      • 1970-01-01
      • 2013-04-08
      • 2017-04-29
      相关资源
      最近更新 更多