摘要:與子生成器是開始引入的新特性。我們把這種一個生成器中調(diào)用的另一個生成器叫做子生成器,而這個子生成器由關(guān)鍵字生成。由于子生成器很常用,所以引入了新的語法來簡化這個代碼。下次,會繼續(xù)對之前的結(jié)果進行乘方,直到結(jié)果超過為止。
Python高級語法中,由一個yield關(guān)鍵詞生成的generator生成器,是精髓中的精髓。它雖然比裝飾器、魔法方法更難懂,但是它強大到我們難以想象的地步:小到簡單的for loop循環(huán),大到代替多線程做服務(wù)器的高并發(fā)處理,都可以基于yield來實現(xiàn)。
理解yield:代替return的yield簡單來說,yield是代替return的另一種方案:
return就像人只有一輩子,一個函數(shù)一旦return,它的生命就結(jié)束了
yield就像有“第二人生”、“第三人生”甚至輪回轉(zhuǎn)世一樣,函數(shù)不但能返回值,“重生”以后還能再接著“上輩子”的記憶繼續(xù)返回值
我的定義:yield在循環(huán)中代替return,每次循環(huán)返回一次值,而不是全部循環(huán)完了才返回值。
yield怎么念?
return我們念“返回xx值”,我建議:yield可以更形象的念為"嘔吐出xx值“,每次嘔一點。
一般我們進行循環(huán)迭代的時候,都必須等待循環(huán)結(jié)束后才return結(jié)果。
數(shù)量小的時候還行,但是如果循環(huán)次數(shù)上百萬?上億?我們要等多久?
如果循環(huán)中不涉及I/O還行,但是如果涉及I/O堵塞,一個堵幾秒,后邊幾百萬個客戶等著呢,銀行柜臺還能不能下班了?
所以這里肯定是要并行處理的。除了傳統(tǒng)的多線程多進程外,我們還可以選擇Generator生成器,也就是由yield代替return,每次循環(huán)都返回值,而不是全部循環(huán)完了才返回結(jié)果。
這樣做的好處就是——極大的節(jié)省了內(nèi)存。如果用return,那么循環(huán)中的所有數(shù)據(jù)都要不斷累計到內(nèi)存里直到循環(huán)結(jié)束,這個不友好。
而yield則是一次一次的返回結(jié)果,就不會在內(nèi)存里累加了。所以數(shù)據(jù)量越大,優(yōu)勢就越明顯。
有多明顯?如果做一百萬的簡單數(shù)字計算,普通的for loop return會增加300MB+的內(nèi)存占用!而用yield一次一次返回,增加的內(nèi)存占用幾乎為0MB!
yield的位置既然yield不是全部循環(huán)完了再返回,而是循環(huán)中每次都返回,所以位置自然不是在for loop之后,而是在loop之中。
先來看一般的for loop返回:
def square(numbers): result = [] for n in numbers: result.append( n**2 ) return result #在for之外
再來看看yield怎么做:
def square(numbers): for n in numbers: yield n**2 #在for之中
可以看到,yield在for loop之中,且函數(shù)完全不需要寫return返回。
這時候如果你print( square([1,2,3]) )得到的就不是直接的結(jié)果,而是一個
如果要使用,就必須一次一次的next(...)來獲取下一個值:
>>> results = square( [1,2,3] ) >>> next( result ) 1 >>> next( result ) 4 >>> next( result ) 9 >>> next( result ) ERROR: StopIteration
這個時候更簡單的做法是:
for r in results: print( r )
因為in這個關(guān)鍵詞自動在后臺為我們調(diào)用生成器的next(..)函數(shù)
什么是generator生成器?
只要我們在一個函數(shù)中用了yield關(guān)鍵字,函數(shù)就會返回一個
yield和generator的關(guān)系,簡單來說就是一個起因一個結(jié)果:只要寫上yield, 其所在的函數(shù)就立馬變成一個
Python中我們使用range()函數(shù)生成數(shù)列非常常用。而xrange()的使用方法、效果幾乎一模一樣,唯一不同的就是——xrange()返回的是生成器,而不是直接的結(jié)果。
如果數(shù)據(jù)量大時,xrange()能極大的減小內(nèi)存占用,帶來卓越的性能提升。
當(dāng)然,幾百、幾千的數(shù)量級,就直接用range好了。
多重yield有時候我們可能會在一個函數(shù)中、或者一個for loop中看到多個yield,這有點不太好理解。
但其實很簡單!
一般情況下,我們寫的:
for n in [1,2,3]: yield n**2
實際上它的本質(zhì)是生成了這個東西:
yield 1**2 yield 2**2 yield 3**2
也就是說,不用for loop,我們自己手寫一個一個的yield,效果也是一樣的。
你每次調(diào)用一次next(..),就得到一個yield后面的值。然后三個yield的第一個就會被劃掉,剩兩個。再調(diào)用一次,再劃掉一個,就剩一個。直到一個都不剩,next(..)就返回異常。
一旦了解這個本質(zhì),我們就能理解一個函數(shù)里寫多個yield是什么意思了。
從多重yield延伸,我們可以開始更進一步了解yield到底做了些什么了。
現(xiàn)在,我們不把yield看作是return的替代品了,而是把它看作是一個suspense暫停符。
即每次程序遇到y(tǒng)ield,都會暫停。當(dāng)你調(diào)用next(..)時候,它再resume繼續(xù)。
比如我們改一下上面的程序:
def func(): yield 1**2 print("Hi, Im A!") yield 2**2 print("Hi, Im B!") yield 3**2 print("Hi, Im C!")
然后我們調(diào)用這個小函數(shù),來看看yield產(chǎn)生的實際效果是什么:
>>> f = func() >>> f>>> next( f ) 1 >>> next( f ) Hi, Im A! 4 >>> next( f ) Hi, Im B! 9 >>> next( f ) Hi, Im C! ERROR: StopIteration
從這里我們可以看到:
第一次調(diào)用生成器的時候,yield之后的打印沒有執(zhí)行。因為程序yield這里暫停了
第二次調(diào)用生成器的時候,第一個yield之后的語句執(zhí)行了,并且再次暫停在第二個yield
第三次調(diào)用生成器的時候,卡在了第三個yield。
第四次調(diào)用生成器的時候,最后一個yield以下的內(nèi)容還是執(zhí)行了,但是因為沒有找到第四個yield,所以報錯。
所以到了這里,如果我們能理解yield作為暫停符的作用,就可以非常靈活的用起來了。
yield from與sub-generator子生成器yield from是Python 3.3開始引入的新特性。
它主要作用就是:當(dāng)我需要在一個生成器函數(shù)中使用另一個生成器時,可以用yield from來簡化語句。
舉例,正常情況下我們可能有這么兩個生成器,第二個調(diào)用第一個:
def gen1(): yield 11 yield 22 yield 33 def gen2(): for g in gen1(): yield g yield 44 yield 55 yield 66
可以看到,我們在gen2()這個生成器中調(diào)用了gen1()的結(jié)果,并把每次獲取到的結(jié)果yield轉(zhuǎn)發(fā)出去,當(dāng)成自己的yield出來的值。
我們把這種一個生成器中調(diào)用的另一個生成器叫做sub-generator子生成器,而這個子生成器由yield from關(guān)鍵字生成。
由于sub-generator子生成器很常用,所以Python引入了新的語法來簡化這個代碼:yield from。
上面gen2()的代碼可以簡化為:
def gen2(): yield from gen1() yield 44 yield 55 yield 66
這樣看起來是不是更"pythonic"了呢?:)
所以只要記住:yield from只是把別人嘔吐出來的值,直接當(dāng)成自己的值嘔吐出去。
遞歸+yield能產(chǎn)生什么?一般我們只是二選一:要不然遞歸,要不然for循環(huán)中yield。有時候yield就可以解決遞歸的問題,但是有時候光用yield并不能解決,還是要用遞歸。
那么怎么既用到遞歸,又用到y(tǒng)ield生成器呢?
參考:Recursion using yield
def func(n): result = n**2 yield result if n < 100: yield from func( result ) for x in func(100): print( x )
上面代碼的邏輯是:如果n小于100,那么每次調(diào)用next(..)的時候,都得到n的乘方。下次next,會繼續(xù)對之前的結(jié)果進行乘方,直到結(jié)果超過100為止。
我們看到代碼里利用了yield from子生成器。因為yield出的值不是直接由變量來,而是由“另一個”函數(shù)得來了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/43207.html
摘要:主程序通過喚起子程序并傳入數(shù)據(jù),子程序處理完后,用將自己掛起,并返回主程序,如此交替進行。通過輪詢或是等事件框架,捕獲返回的事件。從消息隊列中取出記錄,恢復(fù)協(xié)程函數(shù)。然而事實上只有直接操縱的協(xié)程函數(shù)才有可能接觸到這個對象。 首發(fā)于 我的博客 轉(zhuǎn)載請注明出處 寫在前面 本文默認讀者對 Python 生成器 有一定的了解,不了解者請移步至生成器 - 廖雪峰的官方網(wǎng)站。 本文基于 Pyth...
摘要:開始本文主要記錄廖大教程中高級特性這一節(jié)的內(nèi)容,并寫下我的一些理解。廖大的教程中是這樣說的函數(shù)是順序執(zhí)行,遇到語句或者最后一行函數(shù)語句就返回。 前言 用 python 差不多半年多了,從去年暑假開始接觸,從開始的懵逼,到寫了一些小爬蟲總算入門之后,許多作業(yè)也是能用 python 就用 python,基本拋棄了 C++。但是還是有些過于急躁了,能夠?qū)懸恍┖喍痰拇a,但是對于 python...
摘要:常規(guī)的使用來統(tǒng)計一段代碼運行時間的例子輸出結(jié)果總結(jié)其實是一門特別人性化的語言,但凡在工程中經(jīng)常遇到的問題,處理起來比較棘手的模式基本都有對應(yīng)的比較優(yōu)雅的解決方案。 python的高級特性 名詞與翻譯對照表 generator 生成器 iterator 迭代器 collection 集合 pack/unpack 打包/解包 decorator 裝飾器 context manager ...
摘要:示例代碼如下此示例中可以看出,當(dāng)?shù)鹘K止時,通過拋出異常告知迭代器已耗盡。但如果迭代器所指向的數(shù)據(jù)結(jié)構(gòu)在其存在時發(fā)生了插入或刪除操作,則迭代器將可能失效。與的情形類似,對進行任何插入操作也將損壞迭代器。 花下貓語:之前說過,我對于編程語言跟其它學(xué)科的融合非常感興趣,但我還說漏了一點,就是我對于 Python 跟其它編程語言的對比學(xué)習(xí),也很感興趣。所以,我一直希望能聚集一些有其它語言基...
閱讀 2222·2021-11-18 10:02
閱讀 3480·2021-11-15 11:36
閱讀 1116·2019-08-30 14:03
閱讀 725·2019-08-30 11:08
閱讀 2761·2019-08-29 13:20
閱讀 3287·2019-08-29 12:34
閱讀 1375·2019-08-28 18:30
閱讀 1642·2019-08-26 13:34