yield有點類似於return,也是返回數值的作用,但是在使用過程中有很多不一樣的特點。
首先說一下yield的使用背景,是爲了節省內存,當我們使用傳統方法時,比如生成含有十萬個數值的列表,很可能會把內存佔滿,但是yield方法不是全部生成,而是一個一個按照指令生成,不會事先全部生成,所以會省內存。
記住了這個背景,再來理解yield的特性就會好懂了。
- 定義一個函數,如果其中有yield,那麼在函數初始化過程中,只是生成了generator,並不直接執行函數。
- 如果想往下執行,那麼使用__next__()方法,使用後會執行代碼塊中內容。
- __next__()方法會執行到下一個yield處停止,並返回相應數值。
- 如果繼續往下沒有找到下一個yield,編輯器會報錯stopIteration。
來看下代碼
def function():
print('first yield')
yield 1
print('second yield')
yield 2
print('third yield')
yield 3
print('forth yield')
func1 = function()
print('生成器初始化', func1, '\n--------1執行完畢')
print('生成器到第一個yield停止', func1.__next__(), '\n--------2執行完畢')
print('生成器到第二個yield停止', func1.__next__(), '\n--------3執行完畢')
print('生成器到第三個yield停止', func1.__next__(),'\n---------4執行完畢')
print('生成器找不到到第四個yield報錯', func1.__next__(), '\n--------5執行完畢')
輸出:
生成器初始化 <generator object function at 0x1041179a8>
--------1執行完畢 #第一個Print 沒有返回值
first yield
生成器到第一個yield停止 1
--------2執行完畢 #第二個print 輸出了字符串,返回了1
second yield
生成器到第二個yield停止 2
--------3執行完畢 #第三個print 繼續往下執行,輸出了字符串,返回了2
third yield
生成器到第三個yield停止 3
---------4執行完畢 #第4個print ,繼續往下執行,輸出了字符串,返回了3
forth yield #第5個print 繼續往下執行,但是沒找到yield,因此報錯,'-------5執行完畢'沒有執行輸出
Traceback (most recent call last):
File "/Volumes/Others/DM/oldboy/test.py", line 39, in <module>
print('生成器找不到到第四個yield報錯', func1.__next__(), '\n--------5執行完畢')
StopIteration
- send(argv)比__next__()的作用要多一個,既給出指令往下執行next,還要給上一個yield位置傳值argv,_next_()相當於send(None)。
來看下示例:
def function():
print('first yield')
a = yield 1
print(a)
print('second yield')
b = yield 2
print(b)
print('third yield')
c = yield 3
print(c)
print('forth yield')
func1 = function()
print(func1.__next__(), '---------1執行完畢' ) #也可以使用func1.send(None)
print(func1.send('py'),'------2執行完畢')
print(func1.send('th'),'------3執行完畢')
print(func1.send('on'),'------4執行完畢')
輸出:
first yield
1 ---------1執行完畢 #第一個print輸出 返回了1
py
second yield
2 ------2執行完畢 #第二個print輸出 返回了a的賦值'py',返回了2
th
third yield
3 ------3執行完畢 #第三個print輸出 返回了b的賦值'th',返回了3
on
forth yield #第4個print輸出 返回了c的賦值'on' 但是沒找到下一個yield,後面就報錯了
Traceback (most recent call last):
File "/Volumes/Others/DM/oldboy/test.py", line 41, in <module>
print(func1.send('on'),'------4執行完畢')
StopIteration
Process finished with exit code 1
感覺還是很亂,對吧,其實重點理解一下這個賦值語句:
a = yield 1
#這一個賦值語句,分了2步執行,第一步返回值1,這是上一個yield的執行結果,第二步,根據send函數傳過來的值賦給了a,執行到下一個yield結束,這兩步沒有直接關係。
執行順序如圖:
最後就很容易理解協程的概念了(人爲控制程序和代碼的跳轉,以達到快速處理任務不等待的目的),使用yield處理經典的消費者和生產者模型,代碼來自廖雪峯大神的python教程。
def consumer():
r = ''
while True:
n = yield r #把r的值傳給produce()
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None) #啓動generator,此時r爲空,返回了一個空值
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
#跳到了consumer(),把n給到上一個yield位置並往下執行,這裏的r是接收consumer返回的yield r。
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer() #生成了一個generator
produce(c) #把generator當做參數傳入produce函數