在
python
中我們常聽到迭代器和生成器,但是本人分開來介紹,只為告訴大家迭代器和生成器不是一個東西,在上篇文章中我們詳細的介紹過迭代器和可迭代物件,本章重點介紹生成器。
一、生成器的應用場景
對於呼叫一個普通的Python
函式,一般是從函式的第一行程式碼開始執行,結束於return
語句、異常或者函式所有語句執行完畢。一旦函式將控制權交還給呼叫者,就意味著全部結束。函式中做的所有工作以及儲存在區域性變數中的資料都將丟失。再次呼叫這個函式時,一切都將從頭建立。Python
是通過生成器來實現類似於協同程式的概念:生成器可以暫時掛起函式,並保留函式的區域性變數等資料,然後在再次呼叫它的時候,從上次暫停的位置繼續執行下去。
二、一個最簡單的生成器
-
1、使用列表推導式的方式生成一個生成器
from collections.abc import Iterator, Iterableif __name__ == "__main__": # 列表生成式 lis = [x * x for x in range(10)] print(lis) print(isinstance(lis, Iterable)) print(isinstance(lis, Iterator)) # 生成器 generator_ex = (x * x for x in range(10)) print(generator_ex) print(isinstance(generator_ex, Iterable)) print(isinstance(generator_ex, Iterator)) print(next(generator_ex))複製程式碼
-
2、從上面我們可以知道生成器是迭代器,具有迭代器的可迭代和**使用
next()
**函式的功能。記住:生成器是一種特殊的迭代器
三、為什麼要使用生成器函式,而不是直接使用函式
- 1、通過列表生成式可以直接建立一個列表,但是受到記憶體的限制,可能建立一個很大的空間,但是實際情況中用不到那麼多(在C語言中所謂的堆區,沒釋放)
- 2、生成器(
generator
)的定義就是根據列表元素可以動態的分配記憶體空間 - 3、在實際開發過程中,如果我們要讀取一個十幾G大的檔案,如果是直接使用檔案開啟的方式,其實底層的全部載入在記憶體中,這樣造成計算機記憶體消耗,造成計算機卡死的局面,如果我們使用生成器,把大檔案做成文件碎片的方式,每次從中間讀取一點出來,然後再釋放記憶體,這樣就不會對計算機造成卡死的局面。
四、建立生成器函式
-
1、將普通函式的
return
改為yield
就是一個生成器函式def func(): print('====>
first') yield 1 print('====>
second') yield 2 print('====>
third') yield 3 print('====>
end')if __name__ == '__main__': g = func() print(g) # 生成器是一種特殊的迭代器,具有可迭代屬性 for item in g: print(item)複製程式碼
五、協同程式
協同程式(協程)一般來說是指這樣的函式
- 1、彼此間有不同的區域性變數、指令指標,但是共享全域性變數
- 2、可以方便的掛起、恢復,並且有多個入口點和出口點
- 3、多個協同程式間表現為協作執行,如A的執行過程中需要B的結果才能繼續執行。
幾個方法的介紹
-
1、
send(value):
send()
是除了next
外另一個恢復生成器的方法,或者叫往生成器中傳遞引數的方法。是將yield
語句變成了yield
表示式,這意味著yield
現在可以有一個值,而這個值就是在生成器的send
方法被呼叫從而恢復執行時,呼叫send
方法的引數。def test(): i = 1 while i <
5: temp = yield i ** 2 print('temp', temp, '當前i=', i) i += 1 if __name__ == "__main__": t = test() print(next(t)) print(next(t)) print(t.send('hello')) print(t.send('word'))複製程式碼# 執行結果1 # 在main中列印的temp None 當前i= 1 # 在test函式中列印的4 # 在main中列印的temp hello 當前i= 2 # 在test函式中列印的9 # 在main中列印的temp word 當前i= 3 # 在test函式中列印的16 # 在main中列印的複製程式碼
- 呼叫
send
傳入非None
值前,生成器必須處於掛起狀態,否則將丟擲異常 - 第一次呼叫
next
的時候yield 1**2
,返回是1,當時temp
沒有傳遞值是None
- 第二次呼叫
next
的時候yield 2**2
返回是4,當時temp
沒有傳遞值是None
- 第三次呼叫
send
傳送資料,傳送了hello
當時temp=word
,列印出temp
的值,yield 3**2
返回是9 - 第四次和第三次一樣的
- 呼叫
-
2、
close
方法這個方法用於關閉生成器。對關閉的生成器後再次呼叫
next
或send
將丟擲StopIteration
異常
六、yield from
的使用
yield from
是 Python3.3
後新加的語言結構。yield from
的主要功能是開啟雙向通道,把最外層的呼叫方法與最內層的子生成器連線起來。這兩者就可以進行傳送值和返回值了,yeild from
結構的本質是簡化巢狀的生產器,不理解這個是什麼意思的話,下面我將用幾個例子來對其使用方法進行講解
一般場景使用方式
def gene(): for c in 'AB': yield c # 遇到yeild程式返回迴圈,下次從yeild後面開始。 for i in range(3): yield iif __name__ == "__main__": print(list(gene())) # list內部會預激生成器複製程式碼
使用yield..from
的時候
def gene1(): yield from 'ab' yield from range(3)if __name__ == "__main__": print(list(gene1())) 複製程式碼