什么是Python Generator(生成器) ?
我們提供的服務(wù)有:成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、雙塔ssl等。為成百上千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的雙塔網(wǎng)站制作公司
Python Generator(生成器)用于在內(nèi)存資源有限的情況下,把處理大數(shù)據(jù)的任務(wù),分解為一段一段可以管理和處理的數(shù)據(jù)塊(chunk),建立起數(shù)據(jù)流(data pipeline),從而一步一步的解決完大數(shù)據(jù)任務(wù)的技術(shù)。例如,假設(shè)有500G的數(shù)據(jù)待處理,內(nèi)存只有32G,我們可以把數(shù)據(jù)分為200M的數(shù)據(jù)塊,然后借助Python Generator技術(shù),實(shí)現(xiàn)一邊加載數(shù)據(jù)一邊進(jìn)行數(shù)據(jù)處理的效果。
生成器關(guān)鍵字yield 與 函數(shù)返回語句return的區(qū)別 :
return語句 終止函數(shù)運(yùn)行并返回return語句后面的變量值;return語句后面的語句不執(zhí)行。
Python生成器可以由以下兩種方式創(chuàng)建:
對(duì)于程序而言,內(nèi)存也是很重要的,因?yàn)槌绦蛑泻芏鄶?shù)據(jù)都是保存在內(nèi)存中的,如果內(nèi)存中存儲(chǔ)的數(shù)據(jù)過多,那么系統(tǒng)就會(huì)崩潰,這是人們不希望發(fā)生的。
可以采用生成器推導(dǎo)式來解決內(nèi)存不足的問題。例如,利用生成器推導(dǎo)式創(chuàng)建一個(gè)生成器n,數(shù)據(jù)為1~33數(shù)字,可以寫成n = (i for i in range(1, 34))。這樣當(dāng)程序需要一個(gè)數(shù)時(shí),程序才生成數(shù)據(jù),可以節(jié)省內(nèi)存。然而生成器推導(dǎo)式太過簡單,只能用一行代碼的形式實(shí)現(xiàn),如果要?jiǎng)?chuàng)建復(fù)雜的生成器,如創(chuàng)建一個(gè)生成器f,生成前10個(gè)斐波那契數(shù)字,生成器推導(dǎo)式已經(jīng)不能滿足需求了,因?yàn)殪巢瞧鯏?shù)列最開始的兩個(gè)數(shù)都無法賦值。
函數(shù)可以實(shí)現(xiàn)復(fù)雜的功能,然而要節(jié)省內(nèi)存,就需要使用生成器函數(shù)。生成器函數(shù)與普通函數(shù)的區(qū)別是函數(shù)中包含關(guān)鍵字yield。實(shí)際上只要含有yield關(guān)鍵字的函數(shù)就是生成器函數(shù)。
生成器函數(shù)是用函數(shù)實(shí)現(xiàn)生成器。定義生成器函數(shù)的語法格式如下:
def?fib():???#?定義生成器函數(shù)
...?????a,?b?=?1,?1???#?定義初始值
...?????while?True:
...?????????yield?a???#?暫停執(zhí)行,返回一個(gè)新變量值
...?????????a,?b?=?b,?a+b
...
a=?fib()???#?調(diào)用生成器函數(shù)
for?i?in?range(10):
...?????print(next(a))???#?調(diào)用生成器函數(shù)的yield生成值
...
1
1
2
3
5
8
13
21
34
55
def 函數(shù)名(參數(shù)):
函數(shù)體
yield 變量名
函數(shù)體
由語法格式可知,生成器函數(shù)與普通函數(shù)的區(qū)別在于函數(shù)體部分,生成器函數(shù)的函數(shù)體含有“yield 變量名”語句。yield的功能類似于return,return是函數(shù)返回值,yield的功能也是返回變量,但是它僅返回變量而不退出函數(shù),因此,yield可以看作是多次返回變量且不會(huì)退出函數(shù)的return。
在調(diào)用生成器函數(shù)時(shí),寫上函數(shù)名與參數(shù),并通過一個(gè)變量接收返回值,語法格式如下:
變量名 = 函數(shù)名(參數(shù))
調(diào)用生成器函數(shù)的yield生成值的第一種方法如下:
next(變量名)
第二種方法如下:
變量名.__next__()
在掌握了生成器函數(shù)的定義和調(diào)用之后,就可以使用生成器函數(shù)實(shí)現(xiàn)生成前10個(gè)斐波那契數(shù)字的案例了。這個(gè)案例主要分為三步,第一步是定義生成器函數(shù),第二步是調(diào)用并賦值,第三步是打印結(jié)果,代碼如下:
在上述程序中,首先定義生成器函數(shù)fib(),函數(shù)內(nèi)先定義斐波那契數(shù)列的兩個(gè)初始值,再寫一個(gè)while True死循環(huán)。這個(gè)死循環(huán)有些特別,先是用yield生成待使用的數(shù)字,再通過賦值語句“a, b = b, a+b”將b的值賦給a,將a+b的值賦給b,每次循環(huán)都是如此。然后調(diào)用生成器函數(shù)fib(),再調(diào)用生成器函數(shù)的yield生成值,最后打印結(jié)果。由于需要生成前10個(gè)斐波那契數(shù)字,因此可以采用for循環(huán),每循環(huán)一次生成并打印一個(gè)斐波那契數(shù)字,共循環(huán)10次。第一次循環(huán)時(shí),調(diào)用yield生成值a,即1;第二次循環(huán)時(shí),調(diào)用yield生成值a,a被賦值成b的值,即1,而b被賦值成a+b的值,即2;第三次循環(huán)時(shí),調(diào)用yield生成值a,a被賦值成b的值,而此時(shí)b的值是上次賦值的a+b的值,即2……以此類推,就得到了整個(gè)斐波那契數(shù)列。
如果函數(shù)要返回一系列結(jié)果,我們常見的方法就是將結(jié)果放到一份列表中,然后返回給調(diào)用者。比如下面的函數(shù),返回字符串中每個(gè)單詞的首字母在真?zhèn)€字符串中的索引:
運(yùn)行結(jié)果:
上述的結(jié)果完全符合我們的預(yù)期,但 get_word_index 函數(shù)不夠簡潔。下面我們嘗試使用生成器來實(shí)現(xiàn):
運(yùn)行結(jié)果:
改寫之后,不僅運(yùn)行結(jié)果符合要求,由于不需要和 result 列表交互,函數(shù)也變得非常簡潔。下面我們就來詳細(xì)學(xué)習(xí)下生成器吧~
生成器是指使用 yield 表達(dá)式的函數(shù),調(diào)用生成器函數(shù)時(shí),它并不會(huì)真的運(yùn)行,而是會(huì)返回迭代器。每次在這個(gè)迭代器上面調(diào)用內(nèi)置的 next 函數(shù)時(shí),迭代器就會(huì)把生成器推進(jìn)到下一個(gè) yield 表達(dá)式那里。生成器傳給 yield 的值均會(huì)由迭代器返回給調(diào)用者。
此外,如果輸入量非常大,使用列表作為返回值,那么程序就有可能耗盡內(nèi)存并崩潰。相反,使用生成器之后,則可以應(yīng)對(duì)任意長度的輸入數(shù)據(jù)。
例如,下面這個(gè)生成器函數(shù)可以獲取文件中單詞的索引,而不管文件內(nèi)容多大,該函數(shù)執(zhí)行時(shí)消耗的內(nèi)存,只由單行的文本長度決定:
其中 test_generator.txt 中的內(nèi)容如下:
運(yùn)行結(jié)果:
下面這句話特別重要: 生成器函數(shù)返回的迭代器,是由狀態(tài)的,及調(diào)用者不應(yīng)該反復(fù)使用它 。我們那 word_index_iter 來說明:
如果想重復(fù)調(diào)用,請(qǐng)將其封裝成容器:
運(yùn)行結(jié)果:
關(guān)于上述自定義容器的實(shí)現(xiàn)原理,我的另外一篇文章做了詳細(xì)介紹,鏈接奉上:
生成器似乎并不是一個(gè)經(jīng)常被開發(fā)者討論的語法,因此也就沒有它的大兄弟迭代器那么著名。大家不討論它并不是說大家都已經(jīng)對(duì)它熟悉到人盡皆知,與之相反,即使是工作多年的開發(fā)者可能對(duì)生成器的運(yùn)行過程還是知之甚少。這是什么原因?qū)е碌哪兀课也孪氪蟾庞幸韵聨c(diǎn)原因: (1)運(yùn)行流程不同尋常,(2)日常開發(fā)不需要,(3)常常將生成器與迭代器混淆。 生成器的運(yùn)行流程可以按照協(xié)程來理解,也就是說 返回中間結(jié)果,斷點(diǎn)繼續(xù)運(yùn)行 。這與我們通常對(duì)于程序調(diào)用的理解稍有差異。這種運(yùn)行模式是針對(duì)什么樣的需求呢? 一般而言,生成器是應(yīng)用于大量磁盤資源的處理。 比如一個(gè)很大的文件,每次讀取一行,下一次讀取需要以上一次讀取的位置為基礎(chǔ)。下面就通過代碼演示具體看看生成器的運(yùn)行機(jī)制、使用方式以及與迭代器的比較。
什么是生成器?直接用文字描述可能太過抽象,倒不如先運(yùn)行一段代碼,分析這段代碼的運(yùn)行流程,然后總結(jié)出自己對(duì)生成器的理解。
從以上演示可以看出,這段代碼定義了一個(gè)函數(shù),這個(gè)函數(shù)除了yield這個(gè)關(guān)鍵字之外與一般函數(shù)并沒有差異,也就是說生成器的魔法都是這個(gè)yield關(guān)鍵字引起的。 第一點(diǎn),函數(shù)的返回值是一個(gè)生成器對(duì)象。 上述代碼中,直接調(diào)用這個(gè)看似普通的函數(shù),然后將返回值打印出來,發(fā)現(xiàn)返回值是一個(gè)對(duì)象,而并不是普通函數(shù)的返回值。 第二點(diǎn),可以使用next對(duì)這個(gè)生成器對(duì)象進(jìn)行操作 。生成器對(duì)象天然的可以被next函數(shù)調(diào)用,然后返回在yield關(guān)鍵字后面的內(nèi)容。 第三,再次調(diào)用next函數(shù)處理生成器對(duì)象,發(fā)現(xiàn)是從上次yield語句之后繼續(xù)運(yùn)行,直到下一個(gè)yield語句返回。
生成器的運(yùn)行流程確實(shí)詭異,下面還要演示一個(gè)生成器可以執(zhí)行的更加詭異的操作:運(yùn)行過程中向函數(shù)傳參。
返回生成器和next函數(shù)操作生成器已經(jīng)并不奇怪了,但是在函數(shù)運(yùn)行過程中向其傳參還是讓人驚呆了。 調(diào)用生成器的send函數(shù)傳入?yún)?shù),在函數(shù)內(nèi)使用yield語句的返回值接收,然后繼續(xù)運(yùn)行直到下一個(gè)yield語句返回。 以前實(shí)現(xiàn)這種運(yùn)行流程的方式是在函數(shù)中加上一個(gè)從控制臺(tái)獲取數(shù)據(jù)的指令,或者提前將參數(shù)傳入,但是現(xiàn)在不用了,send方式使得傳入的參數(shù)可以隨著讀取到的參數(shù)變化而變化。
很多的開發(fā)者比較容易混淆生成器和迭代器,而迭代器的運(yùn)行過程更加符合一般的程序調(diào)用運(yùn)行流程,因此從親進(jìn)度和使用熟悉度而言,大家對(duì)迭代器更有好感。比如下面演示一個(gè)對(duì)迭代器使用next方法進(jìn)行操作。
從以上演示來看,大家或許會(huì)認(rèn)為迭代器比生成器簡單易用得太多了。不過,如果你了解迭代器的實(shí)現(xiàn)機(jī)制,可能就不會(huì)這么早下結(jié)論了。python內(nèi)置了一些已經(jīng)實(shí)現(xiàn)了的迭代器使用確實(shí)方便,但是如果需要自己去寫一個(gè)迭代器呢?下面這段代碼就帶大家見識(shí)以下迭代器的實(shí)現(xiàn)。
在python中,能被next函數(shù)操作的對(duì)象一定帶有__next__函數(shù)的實(shí)現(xiàn),而能夠被迭代的對(duì)象有必須實(shí)現(xiàn)__iter__函數(shù)??戳诉@么一段操作,相信大家對(duì)迭代器實(shí)現(xiàn)的繁瑣也是深有體會(huì)了,那么生成器的實(shí)現(xiàn)是不是會(huì)讓你覺得更加簡單易用呢?不過千萬別產(chǎn)生一個(gè)誤區(qū),即生成器比迭代器簡單就多用生成器。 在實(shí)際開發(fā)中,如果遇到與大量磁盤文件或者數(shù)據(jù)庫操作相關(guān)的倒是可以使用生成器。但是在其他的任務(wù)中使用生成器難免有炫技,并且使邏輯不清晰而導(dǎo)致可讀性下降的嫌疑。 這大概也能解釋生成器受冷落的原因。不過作為一個(gè)專業(yè)的開發(fā)者,熟悉語言特性是分內(nèi)之事。
到此,關(guān)于生成器的討論就結(jié)束了。本文的notebook版本文件在github上的cnbluegeek/notebook倉庫中共享,歡迎感興趣的朋友前往下載。
迭代器
迭代是Python最強(qiáng)大的功能之一,是訪問集合元素的一種方式。
迭代器是一個(gè)可以記住遍歷的位置的對(duì)象。
迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束,迭代器只能往前不會(huì)后退。
迭代器有兩個(gè)基本的方法:iter()和next()。
生成器
在Python中,使用了yield的函數(shù)被稱為生成器。
跟普通函數(shù)不同的是,生成器是一個(gè)返回迭代器的函數(shù),只能用于迭代操作,更簡單點(diǎn)理解生成器就是一個(gè)迭代器。
在調(diào)用生成器運(yùn)行的過程中,每次遇到y(tǒng)ield時(shí)函數(shù)會(huì)暫停并保存當(dāng)前所有的運(yùn)行信息,返回yield的值,并在下一次執(zhí)行next()方法時(shí)從當(dāng)前位置繼續(xù)運(yùn)行。
調(diào)用一個(gè)生成器函數(shù),返回的是一個(gè)迭代器對(duì)象。
迭代器與生成器之間的區(qū)別:
迭代器是一個(gè)更抽象的概念,任何對(duì)象,如果它的類有NEXTiter方法返回自己本身,對(duì)于string、list、dict、tuple等這類容器對(duì)象,使用for循環(huán)遍歷是很方便的。在后臺(tái)For語言對(duì)容器對(duì)象條用iter()函數(shù),iter()是Python的內(nèi)置函數(shù)。iter()會(huì)返回一個(gè)定義了next()方法迭代器對(duì)象,在容器中逐個(gè)訪問容器的元素,next()也是Python的內(nèi)置函數(shù),next()會(huì)拋出StopIteration異常。
生成器是創(chuàng)新迭代器的簡單而強(qiáng)大的工具,它們寫起來就好像正則函數(shù),只是在需要返回?cái)?shù)據(jù)的時(shí)候使用yield 語句。
迭代器協(xié)議,對(duì)象需要提供next()方法,它要么返回迭代中的下一項(xiàng),要么就引起一個(gè)StopIteration異常,終止迭代。
可迭代對(duì)象,實(shí)現(xiàn)了迭代器協(xié)議對(duì)象。list、tuple、dict都是Iterable可迭代的對(duì)象,但不是Iterator迭代器對(duì)象。
使用場景:
一般我們?cè)谘h(huán)迭代的時(shí)候,都必須等待循環(huán)結(jié)束的時(shí)候才return結(jié)果。
數(shù)量小的時(shí)候還可以,但是如果循環(huán)次數(shù)是上百萬,上億的話,要等多久?
如果循環(huán)不涉及龜速I/O還行,但是如果涉及I/O阻塞,一個(gè)堵幾秒,后面幾百萬幾億個(gè)用戶等著,要等多久?
解決方法:
所以這里肯定要并行處理。除了傳統(tǒng)的多線程多進(jìn)程外,我們還可以用Generator 生成器,由yield代替return,每次循環(huán)都有返回,而不是等所有的執(zhí)行完再返回。
好處:
如果用return,循環(huán)中所有的數(shù)據(jù)都要累計(jì)到內(nèi)存里,直到結(jié)束。這樣不友好。
而yield是一次次的返回結(jié)果,就不會(huì)在內(nèi)存里累計(jì)了。數(shù)據(jù)量越大,越明顯。
生成器概念:
只要在函數(shù)中使用了yield關(guān)鍵字,這個(gè)函數(shù)就會(huì)返回一個(gè)generator的對(duì)象
有了這個(gè)對(duì)象,我們就能使用一系列的操作來控制循環(huán)的結(jié)果
比如 next()就是獲取下一個(gè)結(jié)果
yield和generator的關(guān)系,簡單來說就是一個(gè)起因一個(gè)結(jié)果:只要寫上yield, 其所在的函數(shù)就立馬變成一個(gè)generator object對(duì)象。
range 和 xrange的區(qū)別 :
Python中我們使用range()函數(shù)生成數(shù)列非常常用。而xrange()的使用方法、效果幾乎一模一樣,唯一不同的就是——xrange()返回的是生成器,而不是直接的結(jié)果。
如果數(shù)據(jù)量大時(shí),xrange()能極大的減小內(nèi)存占用,帶來卓越的性能提升。
yield 作為暫停鍵
每次程序遇到y(tǒng)ield時(shí)就會(huì)暫停,當(dāng)調(diào)用next的時(shí)候,就會(huì)繼續(xù)
yield from
如果我們需要在一個(gè)生成器中使用另一個(gè)生成器,可以用yield from 簡化
當(dāng)前名稱:python的生成器函數(shù) python中生成器的作用
標(biāo)題鏈接:http://m.kartarina.com/article16/dodsjgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、關(guān)鍵詞優(yōu)化、微信小程序、App開發(fā)、定制開發(fā)、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)