"返回"之间的差异和"产量"生成器中的stmts

时间:2014-12-24 18:46:56

标签: python for-loop iterator

看看代码:

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表示'收益',1表示'收回')

PS:Python-3.4

2 个答案:

答案 0 :(得分:3)

Python 3.3+生成器中的return语句不能按照您的想法执行。该值不会像普通函数一样返回给调用者,而是作为StopIteration异常的属性添加,生成器会发出信号表示它已完成迭代。你在循环中看到的行为是无关的。

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

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中添加的新功能。

答案 1 :(得分:0)

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

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

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

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语句,因此代码就会运行。