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