【问题标题】:Diff between "return" and "yield" stmts in generators生成器中“return”和“yield”stmts 之间的区别
【发布时间】:2014-12-24 18:46:55
【问题描述】:

看看代码:

def main():
    for p in test1(): print(p)

def test1():
    s = set()
    s.update(range(5))
    for p in s: yield p
    return s

为什么我只有 0,1,2,3,4?输出应该是:0,1,2,3,4 两次(1 代表'yield',1 代表'return')

PS:Python-3.4

【问题讨论】:

  • 你的问题的主要部分是在代码中,因为它偏向一边是不可见的。
  • 你明白for 上的else 是做什么的吗?在the docs 中有解释。
  • 在您完成生成其中的所有元素之后,您还可以在第一个函数中生成实际集合。你需要明白 return 永远不会被执行,即使是,也永远不会被使用
  • 不知道可以将else 子句放在for 循环中。找到了一个关于它的nice blog post
  • @Neft 另一个我经常忘记的模式是try/except/else,其中else 子句只有在没有异常的情况下才会执行。

标签: python for-loop iterator


【解决方案1】:

Python 3.3+ 生成器中的 return 语句并不像您认为的那样。该值不会像在普通函数中那样返回给调用者,而是作为属性添加到生成器引发的StopIteration 异常上,以表明它已完成迭代。您在循环中看到的行为是不相关的。

首先,让我们了解循环行为。这归结为一个简单的事实:当for 循环结束时,循环变量(例如i)不会超出范围:

for i in range(5): # this loop will print 0 through 4
    print(i)

print(i) # this line will print 4 again, since 4 it was the last value assigned to i

您的代码正是这样做的。您正在使用的 else 子句没有什么特别之处,因为您永远不会在循环中使用 break。 (Neftas 的回答解释了附加到循环的 else 的用途。)

至于return的值去哪了,你手动迭代你的生成器就可以找到:

gen = test1()
print(next(gen)) # prints 0
print(next(gen)) # prints 1
print(next(gen)) # prints 2
print(next(gen)) # prints 3
print(next(gen)) # prints 4
print(next(gen)) # prints set([0,1,2,3,4]) from the last yield statement
try:
    next(gen)
except StopIteration as e:
    print(e.value) # prints set([0,1,2,3,4]) from the return statement

这不是很常见的用法。获取返回值的常用方法是在另一个生成器中使用 yield from 表达式的结果:

def test3():
    print(yield from test1())

这是一个生成器,它产生与test1 相同的值,但它也打印出test1 返回的值。

我不认为return 成语在大多数情况下非常有用。 yield from 在递归或其他复杂的生成器中非常有用,但我从来没有发现需要 return 中的一个值。

如果您想了解有关 yield from 表达式和生成器返回值的更多信息,请阅读 PEP 380,它描述了 Python 3.3 中添加的新功能。

【讨论】:

  • 我只是想通过返回“集合”的最后一部分来提高我的应用程序的性能并避免一些混乱。谢谢!
【解决方案2】:

有关生成器中 return 的讨论,请参阅 Blckknght 的回答,这是关于 for 循环中的 else 子句。

如果您不喜欢documentation,我会阅读nice article about else clauses in for loops

它的要点是for 循环中的else 子句是关于完成的,而不是关于条件的。比较:

if 1:
    print "True"
else:
    print "False"

这里,else 子句在比较失败时执行。 但是:

for i in xrange(5):
    if i == 123:
        print "Found it!"
        break
else:
    print "Value not in list"
# output: "Value not in list"

这里,else 子句被执行除非执行流程命中break 语句:

for i in xrange(5):
    if i == 4:
        print "Found it!"
        break
else:
    print "Value not in list"
# output: "Found it!"

如果您删除了break,两个字符串都将被打印。在您的代码中,执行流程将始终到达else 语句,因此那里的代码运行。

【讨论】:

  • 从生成器返回值在 Python 3.3 及更高版本中是合法的。当与 yield from 表达式结合使用时,它有一些(晦涩的)用途。
  • @Blckknght 谢谢,我不知道。我删掉了那部分,但留下了其余部分,以防它可能对某人有用。
猜你喜欢
  • 2016-09-08
  • 2018-01-19
  • 2017-01-09
  • 1970-01-01
  • 2013-06-09
  • 2010-12-14
  • 2016-05-04
  • 2018-09-25
  • 2015-05-18
相关资源
最近更新 更多