弄懂這 6 個(gè)問題,拿下 Python 生成器!

1 什么是可迭代對(duì)象?
可迭代對(duì)象,英文Iterable,是一個(gè)形容詞,這類對(duì)象和Java語言類似,都可看作是一類接口,抽象地描述事物具備怎樣的能力。所以,Iterable自然具備可迭代能力。
如下,常見的list,Iterator等都是Iterable對(duì)象:
In?[33]:?from?collections.abc?import?Iterable,Iterator??????????????????????
In?[34]:?issubclass(list,Iterable)??????????????????????????????????????????
Out[34]:?True
In?[35]:?issubclass(Iterator,Iterable)?
2 什么是一個(gè)生成器?
生成器是可迭代的(Iterable),最簡(jiǎn)單的創(chuàng)建生成器方法是通過一對(duì)(),如下所示:
In?[37]:?g?=?(i*i?for?i?in?[1,4,0])?????????????????????????????????????????
In?[38]:?g??????????????????????????????????????????????????????????????????
Out[38]:??at?0x7fe8956e96d0>
g 是一個(gè)生成器對(duì)象,generator object
獲取生成器對(duì)象的元素,可使用next函數(shù),如下所示,獲取第一個(gè)元素:
In?[39]:?next(g)????????????????????????????????????????????????????????????
Out[39]:?1
獲取第二個(gè)元素:
In?[40]:?next(g)????????????????????????????????????????????????????????????
Out[40]:?16
3 創(chuàng)建生成器的幾種方法?
如上所見,使用一對(duì)()能夠創(chuàng)建一個(gè)生成器對(duì)象。
除此之外,想必大家也都知道,是使用關(guān)鍵字 yield. yield出現(xiàn)在一個(gè)函數(shù)中,運(yùn)行到yield處,返回的對(duì)象便是生成器對(duì)象(generator object).
4 生成器是迭代器嗎?
生成器對(duì)象(generator object)一定也是迭代器對(duì)象(Iterator object),如上面的生成器g,使用內(nèi)置函數(shù)isinstance驗(yàn)證,返回True:
In?[43]:?isinstance(g,Iterator)?????????????????????????????????????????????
Out[43]:?True
因此,它具備一切迭代器的特性,關(guān)于迭代器我們已在上一個(gè)話題討論,簡(jiǎn)而言之,迭代器的幾個(gè)特點(diǎn):
是有去無回的, 迭代器無需提前知道整個(gè)列表的所有元素, 無需加載所有元素到RAM中盡而它是節(jié)省內(nèi)存的(memory-efficient).
生成器同樣具備這些特性。除此之外,它還有一些獨(dú)有的特性,下面跟隨yield 我們便能看出來。
5 如何用一句話描述 yield?

yield 關(guān)鍵字的用法在stackoverflow上也是最熱的問題之一,借用一個(gè)最熱回答中的解釋:
“yield is a keyword that is used like return, except the function will return a generator.
”
yield 是一個(gè)特殊的return, 它返回一個(gè)生成器對(duì)象。
說實(shí)話,理解這些只是皮毛。那么,如何精通yield的用法呢?
6 如何精通yield的用法?
要想精通yield,你必須首先搞清楚一點(diǎn):
當(dāng)你調(diào)用帶有yield的函數(shù)時(shí),函數(shù)并沒有執(zhí)行任何一行,只是返回一個(gè)生成器對(duì)象
為了幫助大家理解,創(chuàng)建一個(gè)帶有yield的函數(shù):
def?gfun():
????mylist?=?range(3)
????for?i?in?mylist:
????????yield?i*i
g?=?gfun()
print(g)
為了加深印象,你可以自己調(diào)試驗(yàn)證一遍,函數(shù)第一行打的斷點(diǎn)始終未被命中。
帶yield的函數(shù)和for結(jié)合使用,第一次調(diào)用上一步創(chuàng)建的生成器對(duì)象后,將會(huì)進(jìn)入函數(shù)體直到遇到yield返回值。
然后,for循環(huán)再進(jìn)入函數(shù)時(shí),直接跑到yield的下一句。直到生成器對(duì)象變空為止。
使用下面代碼演示yield的上面過程:
def?createGenerator():
????mylist?=?range(3)
????for?i?in?mylist:
????????yield?i*i
????????print(i*i)
g?=?createGenerator()
print(g)
for?gi??in?g:
????pass
參考錄制的gif:

7 yield 都有哪些重要價(jià)值?
Python引入yield后,便擁有具備實(shí)現(xiàn)協(xié)程的能力,協(xié)程的確是一種高效的編程模型,關(guān)于協(xié)程的理解,后面專題會(huì)詳細(xì)討論。包括更高級(jí)的功能如異步等,根基都是yield.
8 yield 和 itertools
yield重要性和使用廣泛性還提現(xiàn)在,Python單獨(dú)一個(gè)模塊專門用于管理迭代器和生成器對(duì)象,便是itertools,里面封裝的方法特性后面再討論。
9 yield 使用案例
yield實(shí)現(xiàn)浮點(diǎn)數(shù)步長(zhǎng)的frange:
def?frange(start,end,step):
????i?=?start
????while?i????????yield?round(i,3)
????????i+=step
調(diào)用frange:
for?item?in?frange(10,14,0.8):
????print(item)
結(jié)果:
10
10.8
11.6
12.4
13.2
推薦閱讀
(點(diǎn)擊標(biāo)題可跳轉(zhuǎn)閱讀)
每天增加一個(gè)奇怪的知識(shí)

