Python入門——yield 生成器用法解析

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函數

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章