《Python基礎教程》筆記---生成器
阿新 • • 發佈:2018-12-15
-
生成器
生成器可以說是一個相對較新的python概念,python直譯器會將帶有yield的函式視為生成器處理。雖然說生成器可以幫你編寫出比較優雅的程式碼,但一般編寫的程式,都可以不使用生成器(因為基本可以用其他程式碼替代)
不過,生成器是記憶體友好型的,在處理大資料,佔用大記憶體的情況,還是有很大的用處。
生成器由兩部分組成:生成器的函式和生成器的迭代器
- 生成器的函式:由def語句定義其中包括yield
- 生成器的迭代器:這個函式返回的結果
生成器通過yield生成一個值,每次一個。yield返回一箇中間值給呼叫者後就會暫停執行下面的程式碼,等待下次被重新喚醒,喚醒後便從停止的位置開始繼續執行。與普通函式的差別在於:普通函式使用return返回一個值,而yield可以生成多個值,每次一個。
接下來,用例子來介紹下生成器的具體作用
def pop(conut): a,b=0,1 g=True while g: yield a+b #返回值a+b,並暫停執行下面程式碼 a,b=a+b,b*2 conut=conut-1 if conut<0: g=False p=pop(4) print(p.__next__()) # 輸出1 print(p.__next__()) # 輸出3 print(p.__next__()) # 輸出7 print(p.__next__()) # 輸出15 print(p.__next__()) # 輸出31 print(p.__next__()) # StopIteration錯誤,停止迭代
注意點:是python3.6版本中。生成器的next()方法已經無法使用,相應的替代為了__next__(self); 同時,p=pop(4)返回的是一個generator物件,而p=pop返回的是一個function物件
- 由上述程式碼可知,通過p=pop(4)---->p.__next__(),逐個獲取迭代的值,每次一個。在處理一個巨大的資料集時,相對於使用普通函式一次性獲取所有值的方式而言,採用生成器將有效的降低記憶體佔用的問題。
生成器特性
- __next__(),逐個獲取yield彈出的值
- send(),傳遞引數,實現與生成器的互動
- throw(),用於在生成器中引發異常
- close(),用於關閉生成器
生成器表示式(列表推導的擴充套件)
- 生成器表示式:(表示式 for filename in file)
- 列表推導:[表示式 for filename in file]
生成器表示式返回一個生成器物件,列表推導返回一個列表型別物件,語法上的差別在於,一個外部使用的是圓括號,一個是中括號,列表推導的不足在於:它必須一次性生成所有資料,生成一個列表物件,不適合迭代巨大量的資料集
例子:
#列表推導
f=[i*2 for i in rangr(a,b)]
sum(f)
#生成器表示式
f=(i*2 for i in rangr(a,b))
sum(f)
#生成器表示式簡化,直接在一對既有的圓括號內使用生成器表示式時,可棄掉原有的圓括號
sum(i*2 for i in rangr(a,b))
處理巢狀列表
-
遞迴生成器
處理任意層巢狀的列表(eg:[[[1],2],3,4,[5,[6,7]],8]),可以採用遞迴方式處理。
例子:
def f(nes):
try:
for sub in nes:
for elem in f(sub):
yield elem
except TypeError:
yield nes
- 呼叫函式f時,有兩種可能性:
- 當引數是一個數,而不是一個列表時,try內會引發TypeError異常,直接跳出執行except,生成一個數
- 當引數是一個巢狀列表時,呼叫try內的遞迴函式,遍歷所有子列表並對它們呼叫f函式,隨後展開子列表的所有元素
- 函式f無法對字串物件進行迭代,因為字串物件是可以展開的,同時,字串的第一個元素是一個長度為1的字串,而長度為1的字串的第一元素是字串本身,因此這樣將會導致無窮的遞迴。
改進:
def f(nes):
try:
#nes不是字串物件時,跳過此段程式碼,執行下一段;
#檢測到是一個字串物件時,執行此程式碼,引發TypeError異常,跳出try,直接用外部的except的yield生成這個字串物件
try:nes+''
except TypeError:pass
else:raise TypeError
for sub in nes:
for elem in f(sub):
yield elem
except TypeError:
yield nes
-
普通函式模擬生成器
def f(nes):
result=[]
try:
#nes不是字串物件時,跳過此段程式碼,執行下一段;
#檢測到是一個字串物件時,執行此程式碼,引發TypeError異常,跳出try,直接用外部的except的append()方法將得到的值新增進result列表
try:nes+''
except TypeError:pass
else:raise TypeError
for sub in nes:
for elem in f(sub):
result.append(elem)
except TypeError:
result.append(nes)
return result