python基础(part16)--生成器
鄙人學(xué)習(xí)筆記
開(kāi)發(fā)工具:Spyder
文章目錄
- 生成器generator
- 生成器函數(shù)
- 舉個(gè)例子1(迭代器-->過(guò)渡-->生成器)
- 舉個(gè)例子2
- 內(nèi)置生成器
- 舉個(gè)例子1
- 舉個(gè)例子2
- 枚舉函數(shù)enumerate
- zip
- 生成器表達(dá)式
- 舉個(gè)例子1
- 舉個(gè)例子2
生成器generator
- 定義
能夠動(dòng)態(tài)(循環(huán)一次計(jì)算一次返回一次)提供數(shù)據(jù)的可迭代對(duì)象。
- 作用
在循環(huán)過(guò)程中,按照某種算法推算數(shù)據(jù),不必創(chuàng)建容器,存儲(chǔ)完整的結(jié)果,從而節(jié)省內(nèi)存空間。數(shù)據(jù)量越大,優(yōu)勢(shì)越明顯。
備注:以上生成器的操作,也稱為惰性操作/延遲操作,通俗的講就是在需要的時(shí)候才計(jì)算結(jié)果,而不是一次構(gòu)建出所有結(jié)果。
生成器函數(shù)
- 定義
生成器函數(shù)是含有yield語(yǔ)句的函數(shù),生成器函數(shù)的返回值為生成器對(duì)象。只要我們的函數(shù)中有yield,那么這個(gè)函數(shù)就是生成器函數(shù)。
- 語(yǔ)法
創(chuàng)建生成器函數(shù):
def 生成器函數(shù)名():...yield 數(shù)據(jù)...調(diào)用生成器函數(shù):
my_iter = 生成器函數(shù)名()for 變量名 in my_iter:語(yǔ)句- 說(shuō)明
①調(diào)用生成器函數(shù)將返回一個(gè)生成器對(duì)象my_iter時(shí),不執(zhí)行生成器函數(shù)體。在for循環(huán)內(nèi),調(diào)用__next__()方法時(shí),才執(zhí)行函數(shù)體。
②yield翻譯為”產(chǎn)生”或”生成”。
舉個(gè)例子1(迭代器–>過(guò)渡–>生成器)
我們看一下,下面幾行代碼:
- 運(yùn)行說(shuō)明
首先,當(dāng)程序執(zhí)行第24行代碼,即調(diào)用生成器函數(shù)my_range()時(shí),它不會(huì)去執(zhí)行my_range()函數(shù)體里的內(nèi)容,而是先返回給變量iter01一個(gè)生成器對(duì)象,之后程序會(huì)繼續(xù)向下執(zhí)行for循環(huán)語(yǔ)句:
當(dāng)執(zhí)行第25行語(yǔ)句時(shí),for循環(huán)內(nèi)部調(diào)用了__next__()方法,這時(shí)才開(kāi)始執(zhí)行my_range()函數(shù)體里的內(nèi)容:
當(dāng)執(zhí)行到y(tǒng)ield語(yǔ)句時(shí),程序又返回了,程序?qū)ield右邊的start作為_(kāi)_next__()方法的返回值,給了變量item:
之后的程序運(yùn)行內(nèi)容,不言而喻。就不一一贅述了,有機(jī)會(huì)可以自己在python里,斷點(diǎn)調(diào)試,觀察一下~
舉個(gè)例子2
我想在列表[1, 2, 5, 4, 6, 7, 9]中選出所有偶數(shù),并且通過(guò)生成器函數(shù)來(lái)實(shí)現(xiàn)。
創(chuàng)建生成器函數(shù):
def get_even(target):for item in target:if item % 2 == 0:yield item備用1:我們每次寫生成器函數(shù)時(shí),都要思考yield要返回什么內(nèi)容.
備用2:我們什么時(shí)候使用生成器函數(shù)呢?在我們的方法/函數(shù)需要向外返回多個(gè)結(jié)果時(shí),可以使用生成器函數(shù).
調(diào)用生成器函數(shù):
list01 = [1, 2, 5, 4, 6, 7, 9] iter01 = get_even(list01)for item in iter01:print(item)結(jié)果:
我們其實(shí)不用生成器函數(shù),也可以拿到這個(gè)結(jié)果,比如以下代碼。
創(chuàng)建函數(shù):
def get_even01(target):result = []for item in target:if item %2 == 0:result.append(item)return result調(diào)用函數(shù):
list01 = [1, 2, 5, 4, 6, 7, 9] iter01 = get_even01(list01)for item in iter01:print(item)結(jié)果:
這個(gè)結(jié)果,同上面利用生成器函數(shù),得到的結(jié)果一樣。
那么我們?yōu)樯兑蒙善骱瘮?shù)呢?他的優(yōu)勢(shì)是啥呢?
因?yàn)?#xff0c;如果調(diào)用get_even01()方法,則會(huì)執(zhí)行g(shù)et_even01()方法體,將所有結(jié)果存在內(nèi)存中。但是,如果調(diào)用生成器函數(shù)get_even(), 則不會(huì)執(zhí)行方法體,在內(nèi)存中不會(huì)存儲(chǔ)數(shù)據(jù)。我們用for循環(huán),循環(huán)一次,也就是__next__()一次,計(jì)算一次,返回一次。即,需要一次,做一次。這種生成器函數(shù)的操作就叫做:惰性操作/延遲操作。
所以,在數(shù)據(jù)量非常大的情況下,如果不用生成器函數(shù)進(jìn)行操作,則,我們的內(nèi)存很快就會(huì)被占滿。
內(nèi)置生成器
我們先舉兩個(gè)例子。
舉個(gè)例子1
我們看一段代碼:
結(jié)果:
我們?cè)賹懸粋€(gè)自定義函數(shù)my_enumerate,具有和上述例子中,enumerate函數(shù)所演示出來(lái)的相同的功能:
for循環(huán)測(cè)試一下:
結(jié)果:
嗯!一毛一樣~
舉個(gè)例子2
我們?cè)倏匆欢未a:
list01 = [0, 6, 7] list02 = ["小白", "小黃", "大白"]for item in zip(list01, list02):print(item)結(jié)果:
若我們?cè)诹斜韑ist02中再加一個(gè)元素,使兩個(gè)列表元素不相同:
結(jié)果:
我們?cè)賹懸粋€(gè)自定義函數(shù)my_zip,具有和上述例子中,zip函數(shù)所演示出來(lái)的相同的功能(不考慮兩個(gè)列表元素不相同的情況):
for循環(huán)測(cè)試一下:
list01 = [0, 6, 7] list02 = ["小白", "小黃", "大白"]for item in my_zip(list01, list02):print(item)結(jié)果:
注意:我們稱這種必須由__next__()驅(qū)動(dòng)的函數(shù)(enumerate() / zip()),叫做內(nèi)置生成器。
枚舉函數(shù)enumerate
- 語(yǔ)法
- 作用
遍歷可迭代對(duì)象時(shí),可以將索引與元素組合為一個(gè)元組。
zip
- 語(yǔ)法
- 作用
將多個(gè)可迭代對(duì)象中對(duì)應(yīng)的元素組合成一個(gè)個(gè)元組,生成的元組個(gè)數(shù)由元素?cái)?shù)量最小的可迭代對(duì)象決定。
生成器表達(dá)式
- 定義
用推導(dǎo)式形式創(chuàng)建生成器對(duì)象。
- 語(yǔ)法
舉個(gè)例子1
如果我們有一個(gè)列表list01=[2,3,4,6], 如下圖所示:
我們想得到列表內(nèi)的每個(gè)元素的平方,該咋整呢?
方式1:
方式2(列表推導(dǎo)式):
我們print一下result得到結(jié)果:
備注1:方式1的代碼是列表推導(dǎo)式的傳統(tǒng)寫法。
備注2:對(duì)于推導(dǎo)式,我們還學(xué)習(xí)了字典推到式,集合推導(dǎo)式。但是沒(méi)有元組推導(dǎo)式,這是為什么捏?這是因?yàn)?#xff0c;列表、字典、集合都是可變對(duì)象,可以不斷增加元素。而元組是不可變的。
我們?cè)賹懸欢未a:
這個(gè)可不是”元組推導(dǎo)式”(壓根沒(méi)有元組推導(dǎo)式),這是生成器表達(dá)式。
這時(shí),我們?cè)賞rint一下result:
得到一個(gè)生成器對(duì)象。生成器的本質(zhì)是一個(gè)惰性查找機(jī)制,要調(diào)用它,就要用__next__(), 也就是可以用for循環(huán):
得到結(jié)果:
我們可以寫一個(gè)與這個(gè)生成器表達(dá)式功能相同的生成器函數(shù):
for循環(huán):
得到結(jié)果:
嗯!與利用生成器表達(dá)式運(yùn)行的結(jié)果相同。
這時(shí)候我們就想問(wèn):列表推導(dǎo)式和生成器表達(dá)式有啥區(qū)別?這個(gè)問(wèn)題就相當(dāng)于問(wèn):列表推導(dǎo)式的傳統(tǒng)寫法與生成器函數(shù)有啥不同?
答:區(qū)別就是列表推導(dǎo)式的傳統(tǒng)寫法會(huì)用一個(gè)列表result存儲(chǔ)所有結(jié)論,占用內(nèi)存。而生成器函數(shù)是惰性操作,被調(diào)用一次__next__(), 才會(huì)計(jì)算并返回一次。
舉個(gè)例子2
分別使用列表推導(dǎo)式和生成器表達(dá)式,獲取列表[2, 3, 4, 5, 6]中,大于3的數(shù)據(jù)。
代碼:
list01 = [2, 3, 4, 5, 6]result01 = [item for item in list01 if item > 3] result02 = (item for item in list01 if item > 3)for item in result01:print(item)print("---------")for item in result02:print(item)結(jié)果:
備注:別看列表推導(dǎo)式和生成器表達(dá)式的代碼極其相似,但是他們內(nèi)部卻大有不同,【result01 = [item for item in list01 if item > 3]】是執(zhí)行所有操作,保存所有結(jié)果;【result02 = (item for item in list01 if item > 3】是返回了一個(gè)生成器對(duì)象 。我們也可以通過(guò)斷點(diǎn)調(diào)試,去加深理解。
總結(jié)
以上是生活随笔為你收集整理的python基础(part16)--生成器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python基础(part15)--迭代
- 下一篇: python基础(part17)--函数