Python生成器定義與簡單用法實例分析
本文實例講述了Python生成器定義與簡單用法。分享給大家供大家參考,具體如下:
一、什么是生成器
在Python中,由于受到內(nèi)存的限制,列表容量肯定是有限的。例如我們創(chuàng)建一個包含一億個元素的列表,Python首先會在內(nèi)存中開辟足夠的空間來存儲這個包含一億個元素的列表,然后才允許用戶去使用這個列表,這就可能會導(dǎo)致以下問題:
1、內(nèi)存中沒有足夠的內(nèi)存空間開存儲這個列表,從而導(dǎo)致列表無法創(chuàng)建
2、即使列表成功創(chuàng)建,然而仍會消耗很長的時間,導(dǎo)致程序效率低下
3、若用戶只想訪問列表前面的幾個元素,則后面列表絕大多數(shù)元素占用的空間就都白白浪費了
為了有效解決以上的問題,Python中引入了一種“一邊循環(huán),一邊計算”的新機制,即當用戶需要使用某個對象時,Python才根據(jù)事先設(shè)計好的規(guī)則開辟內(nèi)存空間創(chuàng)建這個對象供用戶使用,而不是像列表一樣事先將所有的對象都創(chuàng)建完畢之后再提供給用戶使用。這種機制在Python中成為生成器(generator)。
二、生成器的創(chuàng)建
A、生成器推到式
與列表推到式類似,只不過生成器推導(dǎo)式使用()而非[],并且最終返回的是生成器而非列表
g=((i+2)**2 for i in range(2,30)) #g是一個生成器 print(g) #g為空,里面包含任何元素
運行結(jié)果:
<generator object <genexpr> at 0x0000000002263150>
B、yield關(guān)鍵字
在一個函數(shù)定義中包含yield關(guān)鍵字,則這個函數(shù)就不再是一個普通的函數(shù),而是一個生成器(generator)
[說明]:yield指令可以暫停一個函數(shù)并返回其中間結(jié)果,使用該指令的函數(shù)將保存執(zhí)行環(huán)境,并在必要時恢復(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)
運行結(jié)果:
<generator object fib at 0x0000000002553150>
[注]:普通函數(shù)和變成生成器的函數(shù)的不同:
普通函數(shù)是順序執(zhí)行的,遇到return或是最后一行函數(shù)語句就返回。而變成生成器的函數(shù)在每次調(diào)用__next__()方法時執(zhí)行,遇到y(tǒng)ield語句返回,再次執(zhí)行時從上次返回的yield語句處繼續(xù)執(zhí)行
f=fib(6)
print(f)
print(f.__next__())
print(f.__next__())
print('暫停一下')
print(f.__next__())
print(f.__next__())
運行結(jié)果:
<generator object fib at 0x00000000025631A8>
1
1
暫停一下
2
3
三、生成器方法(參考:伯樂在線)
1.close()方法:手動關(guān)閉生成器函數(shù),后面的調(diào)用會直接返回StopIteration異常
def func(): yield 1 yield 2 yield 3 g=func() g.__next__() g.close() #手動關(guān)閉生成器 g.__next__() #關(guān)閉后,yield 2和yield 3語句將不再起作用
運行結(jié)果:
Traceback (most recent call last):
File "E:\py3Demo\Hello\generatorDemo.py", line 9, in <module>
g.__next__() #關(guān)閉后,yield 2和yield 3語句將不再起作用
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__()
[流程解釋]:
對于普通的生成器,第一個__next__()方法的調(diào)用相當于啟動生成器,此時會從生成器函數(shù)的第一行開始執(zhí)行,直到第一次執(zhí)行完yield語句(第四行)后,跳出生成器函數(shù)。
當調(diào)用第二個__next__()方法后,會重新進入生成器函數(shù),并從yield語句的下一條語句(第五行)開始執(zhí)行,直到重新運行到y(tǒng)ield語句,執(zhí)行后再次跳出生成器函數(shù)。
后面的__next__()方法調(diào)用以此類推
3.send()方法:接受外部傳入的一個變量,并根據(jù)變量內(nèi)容計算結(jié)果返回到生成器函數(shù)中
[注]:
(1)send()方法和__next__()方法相似,區(qū)別在于send()方法可以傳遞給yield表達式值,而__next__()方法不能傳遞特定的值,只能傳遞None給yield表達式,因此可以將generator.__next__()理解為generator.send(None)
(2)第一次調(diào)用生成器函數(shù)時,必須使用__next__()語句或是send(None),不能使用send發(fā)送一個非None的值給生成器函數(shù),否則會出錯,因為沒有yield語句來接收這個值
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)),從而啟動生成器
print(g.send('aaa'))
print(g.send(3))
print(g.send('end'))
運行結(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.通過g.send(None)或g.__next__()啟動生成器函數(shù),并執(zhí)行到第一個yield語句結(jié)束的位置并將函數(shù)掛起。此時執(zhí)行完了yield語句,但是沒有給receive賦值,因此yield value會輸出value的初始值0
b.g.send('aaa')先將字符串‘a(chǎn)aa'傳入到生成器函數(shù)中并賦值給receive,然后從yield語句的下一句重新開始執(zhí)行函數(shù)(第五句),計算出value的值后返回到while頭部開始新一輪的循環(huán),執(zhí)行到y(tǒng)ield value語句時停止,此時yield value會輸出‘Got:aaa',然后掛起
c.g.send(3)重復(fù)步驟b,最后輸出結(jié)果為‘Got:3'
d.g.send('end')會使程序執(zhí)行break然后跳出循環(huán),從而函數(shù)執(zhí)行完畢,得到StopIteration異常
4.throw()方法:向生成器發(fā)送一個異常。
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))
運行結(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__())會輸出normal value,并停在yield 'normal value2'之前
b.由于執(zhí)行了g.throw(ValueError),所以回跳過后續(xù)的try語句,即yield ‘normal value2'不會執(zhí)行,然后進入到except語句,打印出‘We got ValueError'。之后再次進入到while語句部分,消耗一個yield,輸出normal value
c.print(g.__next__())會執(zhí)行yield ‘normal value2'語句,并停留在執(zhí)行完該語句后的位置
d.g.throw(TypeError)會跳出try語句,因此print('I am here')不會被執(zhí)行,然后打印‘Other errors',并執(zhí)行break語句跳出while循環(huán),然后到達程序結(jié)尾,打印StopIteration異常的信息
四、生成器的運用
import time
def consumer(name):
print('%s準備吃包子啦!' %name)
while True:
baozi=yield #接收send傳的值,并將值賦值給變量baozi
print('包子[%s]來了,被[%s]吃了!' %(baozi,name))
def producer(name):
c1=consumer('A') #把函數(shù)變成一個生成器
c2=consumer('B')
c1.__next__()#調(diào)用這個方法會走到y(tǒng)ield處暫時返回
c2.__next__()
print('開始準備做包子啦!')
for i in range(10):
time.sleep(1)
print('做了一個包子,分成兩半')
c1.send(i)
c2.send(i)
producer('Tomwenxing')
運行結(jié)果:
A準備吃包子啦!
B準備吃包子啦!
開始準備做包子啦!
做了一個包子,分成兩半
包子[0]來了,被[A]吃了!
包子[0]來了,被[B]吃了!
做了一個包子,分成兩半
包子[1]來了,被[A]吃了!
包子[1]來了,被[B]吃了!
做了一個包子,分成兩半
包子[2]來了,被[A]吃了!
包子[2]來了,被[B]吃了!
做了一個包子,分成兩半
包子[3]來了,被[A]吃了!
包子[3]來了,被[B]吃了!
做了一個包子,分成兩半
包子[4]來了,被[A]吃了!
包子[4]來了,被[B]吃了!
做了一個包子,分成兩半
包子[5]來了,被[A]吃了!
包子[5]來了,被[B]吃了!
做了一個包子,分成兩半
包子[6]來了,被[A]吃了!
包子[6]來了,被[B]吃了!
做了一個包子,分成兩半
包子[7]來了,被[A]吃了!
包子[7]來了,被[B]吃了!
做了一個包子,分成兩半
包子[8]來了,被[A]吃了!
包子[8]來了,被[B]吃了!
做了一個包子,分成兩半
包子[9]來了,被[A]吃了!
包子[9]來了,被[B]吃了!
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python加密解密算法與技巧總結(jié)》、《Python編碼操作技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門與進階經(jīng)典教程》
希望本文所述對大家Python程序設(shè)計有所幫助。
相關(guān)文章
如何從csv文件構(gòu)建Tensorflow的數(shù)據(jù)集
這篇文章主要介紹了如何從csv文件構(gòu)建Tensorflow的數(shù)據(jù)集,幫助大家更好的理解和使用Tensorflow,感興趣的朋友可以了解下2020-09-09
Python結(jié)巴中文分詞工具使用過程中遇到的問題及解決方法
這篇文章主要介紹了Python結(jié)巴中文分詞工具使用過程中遇到的問題及解決方法,較為詳細的講述了Python結(jié)巴中文分詞工具的下載、安裝、使用方法及容易出現(xiàn)的問題與相應(yīng)解決方法,需要的朋友可以參考下2017-04-04
對django 2.x版本中models.ForeignKey()外鍵說明介紹
這篇文章主要介紹了對django 2.x版本中models.ForeignKey()外鍵說明介紹,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
基于Python和MoviePy開發(fā)一個視頻管理工具
這篇文章主要為大家詳細介紹了如何基于Python和MoviePy開發(fā)一個視頻管理工具,該工具提供了視頻播放,元數(shù)據(jù)提取,格式轉(zhuǎn)換等功能,有需要的小伙伴可以了解下2025-04-04

