Python生成器定義與簡(jiǎn)單用法實(shí)例分析
本文實(shí)例講述了Python生成器定義與簡(jiǎn)單用法。分享給大家供大家參考,具體如下:
一、什么是生成器
在Python中,由于受到內(nèi)存的限制,列表容量肯定是有限的。例如我們創(chuàng)建一個(gè)包含一億個(gè)元素的列表,Python首先會(huì)在內(nèi)存中開(kāi)辟足夠的空間來(lái)存儲(chǔ)這個(gè)包含一億個(gè)元素的列表,然后才允許用戶去使用這個(gè)列表,這就可能會(huì)導(dǎo)致以下問(wèn)題:
1、內(nèi)存中沒(méi)有足夠的內(nèi)存空間開(kāi)存儲(chǔ)這個(gè)列表,從而導(dǎo)致列表無(wú)法創(chuàng)建
2、即使列表成功創(chuàng)建,然而仍會(huì)消耗很長(zhǎng)的時(shí)間,導(dǎo)致程序效率低下
3、若用戶只想訪問(wèn)列表前面的幾個(gè)元素,則后面列表絕大多數(shù)元素占用的空間就都白白浪費(fèi)了
為了有效解決以上的問(wèn)題,Python中引入了一種“一邊循環(huán),一邊計(jì)算”的新機(jī)制,即當(dāng)用戶需要使用某個(gè)對(duì)象時(shí),Python才根據(jù)事先設(shè)計(jì)好的規(guī)則開(kāi)辟內(nèi)存空間創(chuàng)建這個(gè)對(duì)象供用戶使用,而不是像列表一樣事先將所有的對(duì)象都創(chuàng)建完畢之后再提供給用戶使用。這種機(jī)制在Python中成為生成器(generator)。
二、生成器的創(chuàng)建
A、生成器推到式
與列表推到式類(lèi)似,只不過(guò)生成器推導(dǎo)式使用()而非[],并且最終返回的是生成器而非列表
g=((i+2)**2 for i in range(2,30)) #g是一個(gè)生成器 print(g) #g為空,里面包含任何元素
運(yùn)行結(jié)果:
<generator object <genexpr> at 0x0000000002263150>
B、yield關(guān)鍵字
在一個(gè)函數(shù)定義中包含yield關(guān)鍵字,則這個(gè)函數(shù)就不再是一個(gè)普通的函數(shù),而是一個(gè)生成器(generator)
[說(shuō)明]:yield指令可以暫停一個(gè)函數(shù)并返回其中間結(jié)果,使用該指令的函數(shù)將保存執(zhí)行環(huán)境,并在必要時(shí)恢復(fù)
def fib(max): n,a,b=0,0,1 while n<max: #print(b) yield b a,b=b,a+b n+=1 return 'done' f=fib(6) print(f)
運(yùn)行結(jié)果:
<generator object fib at 0x0000000002553150>
[注]:普通函數(shù)和變成生成器的函數(shù)的不同:
普通函數(shù)是順序執(zhí)行的,遇到return或是最后一行函數(shù)語(yǔ)句就返回。而變成生成器的函數(shù)在每次調(diào)用__next__()方法時(shí)執(zhí)行,遇到y(tǒng)ield語(yǔ)句返回,再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行
f=fib(6) print(f) print(f.__next__()) print(f.__next__()) print('暫停一下') print(f.__next__()) print(f.__next__())
運(yùn)行結(jié)果:
<generator object fib at 0x00000000025631A8>
1
1
暫停一下
2
3
三、生成器方法(參考:伯樂(lè)在線)
1.close()方法:手動(dòng)關(guān)閉生成器函數(shù),后面的調(diào)用會(huì)直接返回StopIteration異常
def func(): yield 1 yield 2 yield 3 g=func() g.__next__() g.close() #手動(dòng)關(guān)閉生成器 g.__next__() #關(guān)閉后,yield 2和yield 3語(yǔ)句將不再起作用
運(yùn)行結(jié)果:
Traceback (most recent call last):
File "E:\py3Demo\Hello\generatorDemo.py", line 9, in <module>
g.__next__() #關(guān)閉后,yield 2和yield 3語(yǔ)句將不再起作用
StopIteration
2.__next__()方法:返回生成器的下一次調(diào)用
def func(): n=1 for i in range(3): yield n n+=1 c=func() a1=c.__next__() a2=c.__next__() a3=c.__next__()
[流程解釋]:
對(duì)于普通的生成器,第一個(gè)__next__()方法的調(diào)用相當(dāng)于啟動(dòng)生成器,此時(shí)會(huì)從生成器函數(shù)的第一行開(kāi)始執(zhí)行,直到第一次執(zhí)行完yield語(yǔ)句(第四行)后,跳出生成器函數(shù)。
當(dāng)調(diào)用第二個(gè)__next__()方法后,會(huì)重新進(jìn)入生成器函數(shù),并從yield語(yǔ)句的下一條語(yǔ)句(第五行)開(kāi)始執(zhí)行,直到重新運(yùn)行到y(tǒng)ield語(yǔ)句,執(zhí)行后再次跳出生成器函數(shù)。
后面的__next__()方法調(diào)用以此類(lèi)推
3.send()方法:接受外部傳入的一個(gè)變量,并根據(jù)變量?jī)?nèi)容計(jì)算結(jié)果返回到生成器函數(shù)中
[注]:
(1)send()方法和__next__()方法相似,區(qū)別在于send()方法可以傳遞給yield表達(dá)式值,而__next__()方法不能傳遞特定的值,只能傳遞None給yield表達(dá)式,因此可以將generator.__next__()理解為generator.send(None)
(2)第一次調(diào)用生成器函數(shù)時(shí),必須使用__next__()語(yǔ)句或是send(None),不能使用send發(fā)送一個(gè)非None的值給生成器函數(shù),否則會(huì)出錯(cuò),因?yàn)闆](méi)有yield語(yǔ)句來(lái)接收這個(gè)值
def gen(): value=0 while True: receive=yield value if receive=='end': break value='Got:%s' %receive g=gen() print(g.__next__()) #或是print(g.send(None)),從而啟動(dòng)生成器 print(g.send('aaa')) print(g.send(3)) print(g.send('end'))
運(yùn)行結(jié)果:
0
Got:aaa
Got:3
Traceback (most recent call last):
File "E:\py3Demo\Hello\generatorDemo.py", line 13, in <module>
print(g.send('end'))
StopIteration
[流程解釋]:
a.通過(guò)g.send(None)或g.__next__()啟動(dòng)生成器函數(shù),并執(zhí)行到第一個(gè)yield語(yǔ)句結(jié)束的位置并將函數(shù)掛起。此時(shí)執(zhí)行完了yield語(yǔ)句,但是沒(méi)有給receive賦值,因此yield value會(huì)輸出value的初始值0
b.g.send('aaa')先將字符串‘a(chǎn)aa'傳入到生成器函數(shù)中并賦值給receive,然后從yield語(yǔ)句的下一句重新開(kāi)始執(zhí)行函數(shù)(第五句),計(jì)算出value的值后返回到while頭部開(kāi)始新一輪的循環(huán),執(zhí)行到y(tǒng)ield value語(yǔ)句時(shí)停止,此時(shí)yield value會(huì)輸出‘Got:aaa',然后掛起
c.g.send(3)重復(fù)步驟b,最后輸出結(jié)果為‘Got:3'
d.g.send('end')會(huì)使程序執(zhí)行break然后跳出循環(huán),從而函數(shù)執(zhí)行完畢,得到StopIteration異常
4.throw()方法:向生成器發(fā)送一個(gè)異常。
def gen(): while True: try: yield 'normal value' #返回中間結(jié)果,此處的yield和return的功能相似 yield 'normal value2' print('I am here') except ValueError: print('We got ValueError') except Exception: print('Other errors') break g=gen() print(g.__next__()) print(g.throw(ValueError)) print(g.__next__()) print(g.throw(TypeError))
運(yùn)行結(jié)果:
Traceback (most recent call last):
File "E:\py3Demo\Hello\generatorDemo.py", line 17, in <module>
print(g.throw(TypeError))
StopIteration
normal value
We got ValueError
normal value
normal value2
Other errors
[解釋]:
a.print(g.__next__())會(huì)輸出normal value,并停在yield 'normal value2'之前
b.由于執(zhí)行了g.throw(ValueError),所以回跳過(guò)后續(xù)的try語(yǔ)句,即yield ‘normal value2'不會(huì)執(zhí)行,然后進(jìn)入到except語(yǔ)句,打印出‘We got ValueError'。之后再次進(jìn)入到while語(yǔ)句部分,消耗一個(gè)yield,輸出normal value
c.print(g.__next__())會(huì)執(zhí)行yield ‘normal value2'語(yǔ)句,并停留在執(zhí)行完該語(yǔ)句后的位置
d.g.throw(TypeError)會(huì)跳出try語(yǔ)句,因此print('I am here')不會(huì)被執(zhí)行,然后打印‘Other errors',并執(zhí)行break語(yǔ)句跳出while循環(huán),然后到達(dá)程序結(jié)尾,打印StopIteration異常的信息
四、生成器的運(yùn)用
import time def consumer(name): print('%s準(zhǔn)備吃包子啦!' %name) while True: baozi=yield #接收send傳的值,并將值賦值給變量baozi print('包子[%s]來(lái)了,被[%s]吃了!' %(baozi,name)) def producer(name): c1=consumer('A') #把函數(shù)變成一個(gè)生成器 c2=consumer('B') c1.__next__()#調(diào)用這個(gè)方法會(huì)走到y(tǒng)ield處暫時(shí)返回 c2.__next__() print('開(kāi)始準(zhǔn)備做包子啦!') for i in range(10): time.sleep(1) print('做了一個(gè)包子,分成兩半') c1.send(i) c2.send(i) producer('Tomwenxing')
運(yùn)行結(jié)果:
A準(zhǔn)備吃包子啦!
B準(zhǔn)備吃包子啦!
開(kāi)始準(zhǔn)備做包子啦!
做了一個(gè)包子,分成兩半
包子[0]來(lái)了,被[A]吃了!
包子[0]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[1]來(lái)了,被[A]吃了!
包子[1]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[2]來(lái)了,被[A]吃了!
包子[2]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[3]來(lái)了,被[A]吃了!
包子[3]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[4]來(lái)了,被[A]吃了!
包子[4]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[5]來(lái)了,被[A]吃了!
包子[5]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[6]來(lái)了,被[A]吃了!
包子[6]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[7]來(lái)了,被[A]吃了!
包子[7]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[8]來(lái)了,被[A]吃了!
包子[8]來(lái)了,被[B]吃了!
做了一個(gè)包子,分成兩半
包子[9]來(lái)了,被[A]吃了!
包子[9]來(lái)了,被[B]吃了!
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python加密解密算法與技巧總結(jié)》、《Python編碼操作技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門(mén)與進(jìn)階經(jīng)典教程》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
相關(guān)文章
python實(shí)現(xiàn)輸入任意一個(gè)大寫(xiě)字母生成金字塔的示例
這篇文章主要介紹了python實(shí)現(xiàn)輸入任意一個(gè)大寫(xiě)字母生成金字塔的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10Python matplotlib畫(huà)圖與中文設(shè)置操作實(shí)例分析
這篇文章主要介紹了Python matplotlib畫(huà)圖與中文設(shè)置操作,結(jié)合實(shí)例形式分析了Python使用matplotlib進(jìn)行圖形繪制及中文設(shè)置相關(guān)操作技巧,需要的朋友可以參考下2019-04-04Numpy數(shù)組的轉(zhuǎn)置和軸交換的實(shí)現(xiàn)
本文主要介紹了Numpy數(shù)組的轉(zhuǎn)置和軸交換的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03Python爬蟲(chóng)爬取疫情數(shù)據(jù)并可視化展示
這篇文章主要介紹了Python利用爬蟲(chóng)爬取疫情數(shù)據(jù)并進(jìn)行可視化的展示,文中的示例代碼講解清晰,對(duì)工作或?qū)W習(xí)有一定的價(jià)值,需要的朋友可以參考一下2021-12-127個(gè)流行的Python強(qiáng)化學(xué)習(xí)算法及代碼實(shí)現(xiàn)詳解
目前流行的強(qiáng)化學(xué)習(xí)算法包括?Q-learning、SARSA、DDPG、A2C、PPO、DQN?和?TRPO。這些算法已被用于在游戲、機(jī)器人和決策制定等各種應(yīng)用中,本文我們將對(duì)其做一個(gè)簡(jiǎn)單的介紹,感興趣的可以學(xué)習(xí)一下2023-01-01淺談Python由__dict__和dir()引發(fā)的一些思考
這篇文章主要介紹了淺談Python由__dict__和dir()引發(fā)的一些思考,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10在python 中split()使用多符號(hào)分割的例子
今天小編就為大家分享一篇在python 中split()使用多符號(hào)分割的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07Django權(quán)限設(shè)置及驗(yàn)證方式
這篇文章主要介紹了Django權(quán)限設(shè)置及驗(yàn)證方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05