python当中的生成器
最近身邊的朋友都在問我迭代器是什么回事,經(jīng)常跟大家一起討論python的迭代器,一點(diǎn)點(diǎn)的我覺著自己有了更深一層的理解。我寫下這篇文章,希望能對(duì)懵懵懂懂的好伙伴有些幫助~
我也不是什么能人,難免說錯(cuò)一些東西,我會(huì)認(rèn)真的把自己理解的說明白,歡迎各路大神批評(píng)指正。
?
生成器是什么??哇!不到哇~~~(眩暈持續(xù)中。。。)
?
生成器: 生成器是一類特殊的迭代器。
他是python提供給我們的一個(gè)功能,提供給我們快速簡(jiǎn)潔的編寫迭代器的功能。
當(dāng)我們需要編寫一個(gè)迭代器的時(shí)候,發(fā)現(xiàn)迭代器很麻煩,我們需要寫__next__和__iter__兩個(gè)方法:
__iter__方法負(fù)責(zé)返回一個(gè)迭代器(在迭代器種返回自己,在可迭代對(duì)象中返回幫助自己迭代的迭代器)
__next__方法做兩件事:
· 1 如果當(dāng)前要獲取的元素沒有超出界限,就返回當(dāng)前元素,然后自己指向?yàn)橄乱粋€(gè)元素等待返回;
2 如果上次反回了最后一個(gè)元素,這一次再調(diào)用next的時(shí)候已經(jīng)沒有元素了,就拋出StopIteration異常。
這兩個(gè)方法中與業(yè)務(wù)邏輯相關(guān)的在next里面,而且next里面拋出異常也與我們想要迭代的元素沒有關(guān)系,我們寫一個(gè)迭代器實(shí)際上是很麻煩的。
在這種情況下,python提供給我們生成器的功能,通過實(shí)現(xiàn)一個(gè)生成器,我們只需要編寫和業(yè)務(wù)邏輯有關(guān)的返回?cái)?shù)據(jù)部分的代碼,而next方法、iter方法和越界拋出異常全都由python幫助我們進(jìn)行封裝,不用我們操心了。這就是生成器!
迭代器又是啥啊!!我去(- 。 -) 好委屈。 迭代器呢,我之前發(fā)過一篇博文,里面詳細(xì)的分享了我對(duì)迭代器的理解。歡迎伙伴們參考~~ 也可以看其他大神的博文哦~一定要搞明白 迭代器是什么否則,生成器也搞不懂的呢!
?
OK!!下面我和大家來探討一下生成器的實(shí)現(xiàn)~~~
?
生成器的實(shí)現(xiàn):
生成器有兩種編寫方法:
1 ( ) 括號(hào)內(nèi) 放入列表推倒表達(dá)式 返回一個(gè)生成器對(duì)象
2 yield 關(guān)鍵字函數(shù)
這兩種方法怎么用呢!! 我們跟大家分享一下 嘻嘻~ 大家要認(rèn)真讀注釋哦
1 ( ) 括號(hào)內(nèi) 放入列表推倒表達(dá)式 返回一個(gè)生成器對(duì)象 # 1 ( 列表推導(dǎo)式 ) #生成前十個(gè)偶數(shù)的列表 list = [ x*2 for x in range(11) ] print(list)#生成前十偶數(shù)的生成器 oddIterator=( x*2 for x in range(11)) print(type(oddIterator)) for num in oddIterator:print(num,end=" ") 從代碼種我們可以看出,普通的列表推導(dǎo)式,放到括號(hào)當(dāng)中,接收的對(duì)象是一個(gè)生成器對(duì)象。它也是一個(gè)迭代器對(duì)象,可以放到for循環(huán)當(dāng)中操作,
也可以用next方法一個(gè)一個(gè)取出元素,還能看到當(dāng)越界的時(shí)候拋出了StopIteration的異常
這些復(fù)雜的東西都被python幫我們封裝了,不需要我們自己操心去處理了。
2 yield 關(guān)鍵字函數(shù)
這個(gè)概念有點(diǎn)頭痛,這什么是yield關(guān)鍵字函數(shù)呢? 不知道呀~
請(qǐng)跟我一起理解:假如我們想寫一個(gè)函數(shù),這個(gè)函數(shù)的功能是:把傳入?yún)?shù)n以內(nèi)的偶數(shù)能給print出來。我們需要用到循環(huán),設(shè)置一個(gè)臨時(shí)變量i 從0自增2到n為止,每一次我們都print(i),這樣我們就能在console中得到n以內(nèi)的全部偶數(shù)。
現(xiàn)在我們更改需求,如果想獲取n以內(nèi)的偶數(shù)的生成器,我們把之前的print(i) 改成yield i ,這樣就實(shí)現(xiàn)了這樣功能的生成器。
先看一段代碼!
# yield關(guān)鍵字函數(shù)#這個(gè)函數(shù)的功能是輸出了0到n的所有偶數(shù) def odd(n):for i in range(0,n+1,2):print(i) odd(10)#現(xiàn)在 我們把這個(gè)方法改成yield關(guān)鍵字函數(shù)的生成器 # 一個(gè)n以內(nèi)偶數(shù)的生成器 def odd(n):for i in range(0,n+1,2):yield i #用gen10獲取一個(gè)生成器的對(duì)象 gen10 = odd(10) # 把生成器對(duì)象放入for循環(huán)當(dāng)中使用 for i in gen10:print(i,end=" ") 從代碼種我們可以看出,把我們平時(shí)想要得到的數(shù)據(jù) 用yield關(guān)鍵字聲明一下,就可以得到生成器了。
python看到y(tǒng)ield會(huì)把這個(gè)函數(shù)幫助我們繼續(xù)封裝,加上next方法和iter方法,并且看到越界后會(huì)幫助我們拋出異常。
這些復(fù)雜的與業(yè)務(wù)邏輯無關(guān)的已經(jīng)無需我們編程者來操心了,python幫助我們完成了。
現(xiàn)在說一下yield i 這句話到底發(fā)生了什么:
首先獲得了一個(gè)迭代器對(duì)象gen = odd(20)
當(dāng)函數(shù)執(zhí)行到y(tǒng)ield i 的時(shí)候 實(shí)際上函數(shù)會(huì)把i的數(shù)值拋出來,我們調(diào)用next(gen)的時(shí)候獲取了yield 后面的值,然后函數(shù)就會(huì)暫停,等待下一次再調(diào)用next(gen)的時(shí)候,函數(shù)從yield繼續(xù)向下執(zhí)行,直到遇到y(tǒng)ield的時(shí)候又返回了i的值,然后函數(shù)再暫停,等待下一次喚醒。
這個(gè)循環(huán)一直做,到函數(shù)結(jié)束的時(shí)候,python幫助咱們拋出了異常。
yield關(guān)鍵字函數(shù)的擴(kuò)展:
返回值:果我們的生成器yield關(guān)鍵字函數(shù)當(dāng)中,結(jié)束時(shí)候自己設(shè)置了返回值,這個(gè)返回值會(huì)被拋出的異常接收,存到了異常對(duì)象的value屬性里面。
兩種喚醒方式:
1 next(gen) 之前討論過,調(diào)用next后,函數(shù)從上一次拋出一個(gè)數(shù)據(jù)暫停之后繼續(xù)執(zhí)行,直到遇到y(tǒng)ield時(shí)候拋出來i返回給next函數(shù)再暫停,等待下一次喚醒。
2 gen.send( mess ) 這個(gè)方法也能夠喚醒生成器函數(shù),并且得到新的yield拋出數(shù)據(jù),不同點(diǎn)是:
如果我們 把上面的yield拋出改成 msg = yield i , 那么我們用send傳入的mess將會(huì)在喚醒的時(shí)候被msg接收到。如果我們用next方法喚醒,則msg接收到None。
很暈是不是! 我們上一段代碼理解一下: 1 # yield關(guān)鍵字函數(shù) 2 #yield關(guān)鍵字函數(shù)的生成器 3 # 一個(gè)n以內(nèi)偶數(shù)的生成器 4 def odd(n): 5 for i in range(0,n+1,2): 6 ''' 7 代碼的執(zhí)行從右向左,當(dāng)遇到y(tǒng)ield的時(shí)候,會(huì)把i拋出給next的調(diào)用返回,然后函數(shù)停在這里 8 下一次外面調(diào)用next或者send方法喚醒的時(shí)候,msg = 開始執(zhí)行,上一次停在了yield i 這里,左邊還沒執(zhí)行 9 然后再碰到y(tǒng)ield i 的時(shí)候把i拋出來再暫停。。。。。。 10 ''' 11 msg = yield i 12 ''' 13 當(dāng)函數(shù)執(zhí)行結(jié)束的時(shí)候python認(rèn)為迭代器結(jié)束了,幫我們拋出異常,返回值會(huì)被異常對(duì)象接收存在了value屬性里面 14 ''' 15 return "哈哈哈" 16 #用gen獲取一個(gè)生成器的對(duì)象 17 gen = odd(5) 18 19 #生成器也是迭代器,用next方法喚醒yield暫停,繼續(xù)向下執(zhí)行 20 print( next(gen) )#0 21 print( next(gen) )#2 22 print( gen.send("傳入數(shù)據(jù)") )#傳入數(shù)據(jù) 4 這個(gè)時(shí)候 在函數(shù)里面會(huì)打印出來傳入的 “傳入數(shù)據(jù)”, 并返回了下一次的i 也就是4 然后暫停 23 24 #這時(shí)候不論next還是send,迭代器都已經(jīng)結(jié)束了 python會(huì)幫我們拋出異常,函數(shù)的返回值會(huì)被異常對(duì)象接收存在value屬性里 25 try : 26 print( next(gen) ) 27 except StopIteration as e : 28 print(e.value) #會(huì)打印出 哈哈哈, 也就是odd函數(shù)的返回值
?
其實(shí)到這里 知識(shí)點(diǎn)就已經(jīng)全部結(jié)束了。 我們?cè)賮砜偨Y(jié)一下:
生成器有兩種實(shí)現(xiàn)方式:
1 () 括號(hào)內(nèi) 放入 列表生成式
2 yield 關(guān)鍵字函數(shù):正常寫一個(gè)業(yè)務(wù)邏輯函數(shù),把想迭代的數(shù)據(jù)用yield關(guān)鍵字聲明。函數(shù)執(zhí)行到y(tǒng)ield關(guān)鍵字會(huì)把后面的數(shù)值拋出去,然后暫停,等待下一次喚醒。
兩種喚醒方式: gen = 生成器函數(shù)() ? 我們拿到一個(gè)生成器對(duì)象gen
1 next(gen) ?能夠喚醒上一次暫停,函數(shù)會(huì)從上一次拋出數(shù)之后繼續(xù)執(zhí)行到再次遇見yield i 把i拋回來 后再暫停
2 gen.send(mess) 喚醒上一次暫停,并且把mess傳入給接收yield 的變量,讓我年后函數(shù)繼續(xù)執(zhí)行遇到 msg = yield i ?的時(shí)候把i拋出來返回,再暫停。
?
?
生成器實(shí)質(zhì): 它是python提供給我們快速寫一個(gè)迭代器的功能。我們只關(guān)心業(yè)務(wù)邏輯,把功能實(shí)現(xiàn)了,至于迭代器內(nèi)部的iter方法和next方法已經(jīng)不用我們操心了,迭代過后的拋出異常也為我們封裝好好了。
因?yàn)樗鼤?huì)被封裝成迭代器,所以我們可以把生成器對(duì)象放入for in 循環(huán)中,也可以用next() 方法去獲取元素!!
?
?
OK啦!! 這些知識(shí)點(diǎn)晦澀難懂,如果讀不懂的伙伴們,希望你們認(rèn)真學(xué)習(xí)一些迭代器的知識(shí),這樣才能看懂生成器哦~~可以參考我之前的博文,也可以參考其他大神的博文哦~
?
謝謝觀賞,希望對(duì)大家有幫助!么么噠~
嘻嘻
?
轉(zhuǎn)載于:https://www.cnblogs.com/Lin-Yi/p/7298136.html
總結(jié)
以上是生活随笔為你收集整理的python当中的生成器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 读书笔记九:TCP/IP详解之广播和多播
- 下一篇: python模块安装路径