Python中"yield"的行为
我在学习Python中的yield
关键字,并试着理解运行这个示例:
def countfrom(n):
while True:
print "before yield"
yield n
n += 1
print "after yield"
for i in countfrom(10):
print "enter for loop"
if i <= 20:
print i
else:
break
输出结果是:
before yield
enter for loop
10
after yield
before yield
enter for loop
11
after yield
before yield
enter for loop
12
after yield
before yield
enter for loop
13
after yield
before yield
enter for loop
14
after yield
before yield
enter for loop
15
after yield
before yield
enter for loop
16
after yield
before yield
enter for loop
17
after yield
before yield
enter for loop
18
after yield
before yield
enter for loop
19
after yield
before yield
enter for loop
20
after yield
before yield
enter for loop
看起来yield
会返回指定的值,并且会继续执行这个函数直到结束(可能是在一个并行线程中)。我的理解对吗?
如果你能在回答中不提到“生成器”,我会很感激,因为我想一步一步地理解。
7 个回答
6
要理解yield
这个语句的意思,必须提到生成器;这就像你想解释石头是什么,却不提岩石一样。简单来说,yield
语句的作用是把一个普通的函数变成一个生成器。
在这里你可以找到详细的说明:http://docs.python.org/reference/simple_stmts.html#the-yield-statement
简单来说,它的解释是:
- 当你调用一个使用了
yield
语句的函数时,它会返回一个“生成器迭代器”,这个迭代器有一个.next()
方法(这是可迭代对象的标准方法)。 - 每次调用生成器的
.next()
方法时(比如用for循环遍历这个对象),函数会执行直到遇到第一个yield
。然后函数会暂停执行,并把一个值作为.next()
方法的返回值。 - 下次调用
.next()
时,函数会继续执行,直到下一个yield
,以此类推,直到函数返回一个值。
这样做的一些好处是:
- 内存使用更少,因为内存只为当前的返回值分配,而不是为整个返回值列表分配(如果返回一个列表的话)。
- 可以“实时”返回结果,生成的结果可以立即传给调用者,而不需要等到所有结果都生成完毕(我用这个方法来返回正在运行的进程的输出)。
14
不,这里只有一个线程。
在这个循环的每一次迭代中,都会运行你的 countFrom
函数,直到它产生一个结果(yield)或者返回(return)。在产生结果之后,循环的主体会再次运行,然后当新的迭代开始时,countFrom
函数会从它上次停止的地方继续运行,直到再次产生结果(或返回)。
这个修改过的例子会更清楚地展示执行的路径。
def countfrom(n):
while n <= 12:
print "before yield, n = ", n
yield n
n += 1
print "after yield, n = ", n
for i in countfrom(10):
print "enter for loop, i = ", i
print i
print "end of for loop iteration, i = ", i
输出
before yield, n = 10
enter for loop, i = 10
10
end of for loop iteration, i = 10
after yield, n = 11
before yield, n = 11
enter for loop, i = 11
11
end of for loop iteration, i = 11
after yield, n = 12
before yield, n = 12
enter for loop, i = 12
12
end of for loop iteration, i = 12
after yield, n = 13
38
你可以把它想象成,当一个函数遇到yield
的时候,它就像是“暂停”了一样。下次你再调用这个函数时,它会在yield
之后继续执行,并且会保持它暂停时的状态。