通過列表生成式,我們可以直接創(chuàng)建一個列表。但是迷雪,受到內(nèi)存限制,列表容量肯定是有限的巷挥。而且,創(chuàng)建一個包含100萬個元素的列表验靡,
不僅占用很大的存儲空間倍宾,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數(shù)元素占用的空間都白白浪費了胜嗓。
所以高职,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環(huán)的過程中不斷推算出后續(xù)的元素呢兼蕊?這樣就不必創(chuàng)建完整的list初厚,從而節(jié)省大量的空間。在Python中孙技,這種一邊循環(huán)一邊計算的機制产禾,稱為生成器(Generator)。
生成器(generator)就是迭代器(iterator)的一種,以更優(yōu)雅的方式實現(xiàn)的iterator牵啦,而且完全可以像使用iterator一樣使用generator亚情。當然除了定義,定義一個iterator哈雏,你需要分別實現(xiàn)_ _ iter _ _ ()方法和 _ _ next_ _()方法,但generator只需要一個小小的yield
iterator通過__next__()方法實現(xiàn)了每次調(diào)用,返回一個單一值的功能.而yield就是實現(xiàn)generator的__next__()方法的關(guān)鍵!
第一次調(diào)用next()方法時,函數(shù)似乎執(zhí)行到y(tǒng)ield 1,就暫停了.然后再次調(diào)用next()時,函數(shù)從yield 1之后開始執(zhí)行的,并再次暫停.第三次調(diào)用next(),從第二次暫停的地方開始執(zhí)行.第四次,拋出StopIteration異常.
將yield理解成一個中斷服務(wù)子程序的斷點,沒錯,是中斷服務(wù)子程序的斷點.我們每次對一個generator對象調(diào)用next()時,函數(shù)內(nèi)部代碼執(zhí)行到”斷點”yield,然后返回這一部分的結(jié)果,并保存上下文環(huán)境,”中斷”返回.
generator 其實有2種調(diào)用方法(恢復(fù)執(zhí)行)楞件,即通過send(value)方法將value作為yield表達式的當前值。你可以用該值再對其他變量進行賦值裳瘪。當我們調(diào)用send(value)方法時土浸,generator正由于yield的緣故被暫停了。此時彭羹,send(value)方法傳入的值作為yield表達式的值,函數(shù)中又將該值賦給了變量s黄伊,然后print函數(shù)打印s,循環(huán)再遇到y(tǒng)ield,暫停返回派殷。
def foo(value=None):
????while 1:
????????value = (yield value)
????????print("the value is{0}".format(value))
????????if value:
????????value += 1
>>> g = foo(1)
>>> next(g)
1
>>> g.send(2)
the value is2
3
>>> g.send(4)
the value is4
5
>>> next(g)
the value isNone
>>>
第一次調(diào)用next()方法,執(zhí)行到y(tǒng)ield value表達式,保存上下文環(huán)境暫停返回1.第二次調(diào)用send(value)方法,從value = yield開始,打印,再次遇到y(tǒng)ield value暫停返回.后續(xù)的調(diào)用send(value)或next()都不外如是.
g = (i for i in range(10))>>> type(g)
<class 'generator'>
>>> g.gi_frame.f_lasti
-1
>>> g.send(None)
0
>>> g.gi_frame.f_lasti
8
>>> len(g.gi_code.co_code)
18
>>>
堆棧幀有個 “l(fā)ast instruction”(FIXME: translate this or not?) 指針还最,指向最近執(zhí)行的那條指令墓阀。剛開始的時候 last instruction 指針是 -1,意味著生成器尚未開始:
當我們調(diào)用?send?時拓轻,生成器達到第一個?yield?處然后暫停執(zhí)行斯撮。send?的返回值是 0,這是因為?g把 0 傳給了?yield?表達式
現(xiàn)在生成器的指令指針(instruction pointer)向前移動了 8 個字節(jié)碼扶叉,這些是編譯好的18字節(jié)的 Python 代碼的一部分
生成器可以在任何時候被任何函數(shù)恢復(fù)執(zhí)行勿锅,因為它的堆棧幀實際上不在堆棧上——它在堆(內(nèi)存)上。生成器在調(diào)用調(diào)用層次結(jié)構(gòu)中的位置不是固定的辜梳,它不需要遵循常規(guī)函數(shù)執(zhí)行時遵循的先進后出順序粱甫。生成器被是被解放了的,它像云一樣浮動作瞄。
總結(jié):
可迭代對象(Iterable)是實現(xiàn)了__iter__()方法的對象,通過調(diào)用iter()方法可以獲得一個迭代器(Iterator)
迭代器(Iterator)是實現(xiàn)了__iter__()和__next__()的對象
for ... in ...的迭代,實際是將可迭代對象轉(zhuǎn)換成迭代器,再重復(fù)調(diào)用next()方法實現(xiàn)的
生成器(generator)是一個特殊的迭代器,它的實現(xiàn)更簡單優(yōu)雅.
yield是生成器實現(xiàn)__next__()方法的關(guān)鍵.它作為生成器執(zhí)行的暫停恢復(fù)點,可以對yield表達式進行賦值,也可以將yield表達式的值返回.
文章參考來源:http://kissg.me/2016/04/09/python-generator-yield/