yield
python中yield的用法很像return,都是提供一個返回值,但是yield和return的最大區別在於,return一旦返回,則代碼段執行結束,但是yield在返回值以后,會交出CUP的使用權,代碼段並沒有直接結束,而是在此處中斷,當調用send()或者next()方法之后,yield可以從之前中斷的地方繼續執行。
在一個函數中,使用yield關鍵字,則當前的函數會變成生成器。
下面生成一個斐波那契數列。
def fib(n):
index = 0
a = 0
b = 1
while index < n:
yield b
a,b = b, a+b
index += 1
- 生成器對象
fib = fib(100)
print(fib)
打印出來的結果是一個生成器對象,並沒有直接把我們想要的值打印出來。
- next()方法
fib = fib(100)
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
它的執行順序是這樣的,每次yield返回之后,程序將會中斷,當出現next(fib)之后,程序將會從之前中斷的地方繼續執行。 python新版本中,不再提供fib.next()
方法。
- send()方法
使用send()方法允許我們向生成器中傳值。
import time
def fib(n):
index = 0
a = 0
b = 1
while index < n:
sleep = yield b
print('等待%s秒' %sleep)
time.sleep(sleep)
a,b = b, a+b
index += 1
fib = fib(20)
print(fib.send(None)) # 效果等同於print(next(fib))
print(fib.send(2))
print(fib.send(2))
print(fib.send(2))
print(fib.send(2))
執行順序如下:
首先,創建生成器對象
調用fib.send(None)方法,此處作用與next(fib)相同,程序返回當前b的值1, 程序中斷。
調用fib.send(2)方法,程序被喚醒,將2傳遞給yield之前的變量sleep,程序繼續運行,直到遇到yield將新的b返回,程序再次中斷。
如此繼續下去,直到程序結束。
yield from
前面的都是單一層次的生成器,並沒有嵌套,如果是多個生成器嵌套會怎么樣呢,下面是一個例子。
def fun_inner():
i = 0
while True:
i = yield i
def fun_outer():
a = 0
b = 1
inner = fun_inner()
inner.send(None)
while True:
a = inner.send(b)
b = yield a
if __name__ == '__main__':
outer = fun_outer()
outer.send(None)
for i in range(5):
print(outer.send(i))
在兩層嵌套的情況下,值的傳遞方式是,先把值傳遞給外層生成器,外層生成器再將值傳遞給外層生成器,內層生成器在將值反向傳遞給外層生成器,最終yield出結果。如果嵌套的層次更多,傳遞將會越麻煩。
下面是yield from的實現方式:
def fun_inner():
i = 0
while True:
i = yield i
def fun_outer():
yield from fun_inner()
if __name__ == '__main__':
outer = fun_outer()
outer.send(None)
for i in range(5):
print(outer.send(i))
效果是一樣的,但是明顯的代碼量減少了,嵌套傳值的時候,並不需要我們手動實現。