日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python之路——迭代器和生成器

發(fā)布時(shí)間:2023/12/20 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python之路——迭代器和生成器 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

 楔子

  假如我現(xiàn)在有一個(gè)列表l=['a','b','c','d','e'],我想取列表中的內(nèi)容,有幾種方式?

  首先,我可以通過索引取值l[0],其次我們是不是還可以用for循環(huán)來取值呀?

  你有沒有仔細(xì)思考過,用索引取值和for循環(huán)取值是有著微妙區(qū)別的。

  如果用索引取值,你可以取到任意位置的值,前提是你要知道這個(gè)值在什么位置。

  如果用for循環(huán)來取值,我們把每一個(gè)值都取到,不需要關(guān)心每一個(gè)值的位置,因?yàn)橹荒茼樞虻娜≈?#xff0c;并不能跳過任何一個(gè)直接去取其他位置的值。

  但你有沒有想過,我們?yōu)槭裁纯梢允褂胒or循環(huán)來取值?

  for循環(huán)內(nèi)部是怎么工作的呢?

 迭代器

返回頂部

 python中的for循環(huán)

  要了解for循環(huán)是怎么回事兒,咱們還是要從代碼的角度出發(fā)。

  首先,我們對(duì)一個(gè)列表進(jìn)行for循環(huán)。

for i in [1,2,3,4]: print(i)

  上面這段代碼肯定是沒有問題的,但是我們換一種情況,來循環(huán)一個(gè)數(shù)字1234試試

for i in 1234print(i) 結(jié)果: Traceback (most recent call last):File "test.py", line 4, in <module>for i in 1234: TypeError: 'int' object is not iterable

  看,報(bào)錯(cuò)了!報(bào)了什么錯(cuò)呢?“TypeError: 'int' object is not iterable”,說int類型不是一個(gè)iterable,那這個(gè)iterable是個(gè)啥?

  

  假如你不知道什么是iterable,我們可以翻翻詞典,首先得到一個(gè)中文的解釋,盡管翻譯過來了你可能也不知道,但是沒關(guān)系,我會(huì)帶著你一步一步來分析。

返回頂部

  迭代和可迭代協(xié)議

  什么叫迭代

  現(xiàn)在,我們已經(jīng)獲得了一個(gè)新線索,有一個(gè)叫做“可迭代的”概念。

  首先,我們從報(bào)錯(cuò)來分析,好像之所以1234不可以for循環(huán),是因?yàn)樗豢傻?。那么如果“可迭代?#xff0c;就應(yīng)該可以被for循環(huán)了。

  這個(gè)我們知道呀,字符串、列表、元組、字典、集合都可以被for循環(huán),說明他們都是可迭代的

  我們?cè)趺磥碜C明這一點(diǎn)呢?

from collections import Iterablel = [1,2,3,4] t = (1,2,3,4) d = {1:2,3:4} s = {1,2,3,4} print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(s,Iterable))

  結(jié)合我們使用for循環(huán)取值的現(xiàn)象,再從字面上理解一下,其實(shí)迭代就是我們剛剛說的,可以將某個(gè)數(shù)據(jù)集內(nèi)的數(shù)據(jù)“一個(gè)挨著一個(gè)的取出來”,就叫做迭代

  可迭代協(xié)議

  我們現(xiàn)在是從結(jié)果分析原因,能被for循環(huán)的就是“可迭代的”,但是如果正著想,for怎么知道誰是可迭代的呢?

  假如我們自己寫了一個(gè)數(shù)據(jù)類型,希望這個(gè)數(shù)據(jù)類型里的東西也可以使用for被一個(gè)一個(gè)的取出來,那我們就必須滿足for的要求。這個(gè)要求就叫做“協(xié)議”。

  可以被迭代要滿足的要求就叫做可迭代協(xié)議。可迭代協(xié)議的定義非常簡(jiǎn)單,就是內(nèi)部實(shí)現(xiàn)了__iter__方法。

  接下來我們就來驗(yàn)證一下:

print(dir([1,2])) print(dir((2,3))) print(dir({1:2})) print(dir({1,2})) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update'] 結(jié)果

  總結(jié)一下我們現(xiàn)在所知道的:可以被for循環(huán)的都是可迭代的,要想可迭代,內(nèi)部必須有一個(gè)__iter__方法。

  接著分析,__iter__方法做了什么事情呢?

print([1,2].__iter__())結(jié)果 <list_iterator object at 0x1024784a8>

  執(zhí)行了list([1,2])的__iter__方法,我們好像得到了一個(gè)list_iterator,現(xiàn)在我們又得到了一個(gè)新名詞——iterator。

  

  iterator,這里給我們標(biāo)出來了,是一個(gè)計(jì)算機(jī)中的專屬名詞,叫做迭代器。?

  迭代器協(xié)議

  既什么叫“可迭代”之后,又一個(gè)歷史新難題,什么叫“迭代器”?

  雖然我們不知道什么叫迭代器,但是我們現(xiàn)在已經(jīng)有一個(gè)迭代器了,這個(gè)迭代器是一個(gè)列表的迭代器。

  我們來看看這個(gè)列表的迭代器比起列表來說實(shí)現(xiàn)了哪些新方法,這樣就能揭開迭代器的神秘面紗了吧?

''' dir([1,2].__iter__())是列表迭代器中實(shí)現(xiàn)的所有方法,dir([1,2])是列表中實(shí)現(xiàn)的所有方法,都是以列表的形式返回給我們的,為了看的更清楚,我們分別把他們轉(zhuǎn)換成集合,
然后取差集。
''' #print(dir([1,2].__iter__())) #print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2])))結(jié)果: {'__length_hint__', '__next__', '__setstate__'}

  我們看到在列表迭代器中多了三個(gè)方法,那么這三個(gè)方法都分別做了什么事呢?

iter_l = [1,2,3,4,5,6].__iter__() #獲取迭代器中元素的長(zhǎng)度 print(iter_l.__length_hint__()) #根據(jù)索引值指定從哪里開始迭代 print('*',iter_l.__setstate__(4)) #一個(gè)一個(gè)的取值 print('**',iter_l.__next__()) print('***',iter_l.__next__())

  這三個(gè)方法中,能讓我們一個(gè)一個(gè)取值的神奇方法是誰?

  沒錯(cuò)!就是__next__

  在for循環(huán)中,就是在內(nèi)部調(diào)用了__next__方法才能取到一個(gè)一個(gè)的值。

  那接下來我們就用迭代器的next方法來寫一個(gè)不依賴for的遍歷。

l = [1,2,3,4] l_iter = l.__iter__() item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item)

  這是一段會(huì)報(bào)錯(cuò)的代碼,如果我們一直取next取到迭代器里已經(jīng)沒有元素了,就會(huì)拋出一個(gè)異常StopIteration,告訴我們,列表中已經(jīng)沒有有效的元素了。

  這個(gè)時(shí)候,我們就要使用異常處理機(jī)制來把這個(gè)異常處理掉。

l = [1,2,3,4] l_iter = l.__iter__() while True:try:item = l_iter.__next__()print(item)except StopIteration:break

  那現(xiàn)在我們就使用while循環(huán)實(shí)現(xiàn)了原本for循環(huán)做的事情,我們是從誰那兒獲取一個(gè)一個(gè)的值呀?是不是就是l_iter?好了,這個(gè)l_iter就是一個(gè)迭代器。

  迭代器遵循迭代器協(xié)議:必須擁有__iter__方法和__next__方法。

  還賬:next和iter方法

如此一來,關(guān)于迭代器和生成器的方法我們就還清了兩個(gè),最后我們來看看range()是個(gè)啥。首先,它肯定是一個(gè)可迭代的對(duì)象,但是它是否是一個(gè)迭代器?我們來測(cè)試一下

print('__next__' in dir(range(12))) #查看'__next__'是不是在range()方法執(zhí)行之后內(nèi)部是否有__next__ print('__iter__' in dir(range(12))) #查看'__next__'是不是在range()方法執(zhí)行之后內(nèi)部是否有__next__from collections import Iterator print(isinstance(range(100000000),Iterator)) #驗(yàn)證range執(zhí)行之后得到的結(jié)果不是一個(gè)迭代器 range函數(shù)的返回值是一個(gè)可迭代對(duì)象

?

  為什么要有for循環(huán)

  基于上面講的列表這一大堆遍歷方式,聰明的你立馬看除了端倪,于是你不知死活大聲喊道,你這不逗我玩呢么,有了下標(biāo)的訪問方式,我可以這樣遍歷一個(gè)列表啊

l=[1,2,3]index=0 while index < len(l):print(l[index])index+=1#要毛線for循環(huán),要毛線可迭代,要毛線迭代器

  沒錯(cuò),序列類型字符串,列表,元組都有下標(biāo),你用上述的方式訪問,perfect!但是你可曾想過非序列類型像字典,集合,文件對(duì)象的感受,所以嘛,年輕人,for循環(huán)就   是基于迭代器協(xié)議提供了一個(gè)統(tǒng)一的可以遍歷所有對(duì)象的方法,即在遍歷之前,先調(diào)用對(duì)象的__iter__方法將其轉(zhuǎn)換成一個(gè)迭代器,然后使用迭代器協(xié)議去實(shí)現(xiàn)循環(huán)訪   問,這樣所有的對(duì)象就都可以通過for循環(huán)來遍歷了,而且你看到的效果也確實(shí)如此,這就是無所不能的for循環(huán),覺悟吧,年輕人

  生成器

返回頂部

  初識(shí)生成器

  我們知道的迭代器有兩種:一種是調(diào)用方法直接返回的,一種是可迭代對(duì)象通過執(zhí)行iter方法得到的,迭代器有的好處是可以節(jié)省內(nèi)存。

  如果在某些情況下,我們也需要節(jié)省內(nèi)存,就只能自己寫。我們自己寫的這個(gè)能實(shí)現(xiàn)迭代器功能的東西就叫生成器。

?

  Python中提供的生成器:

  1.生成器函數(shù):常規(guī)函數(shù)定義,但是,使用yield語句而不是return語句返回結(jié)果。yield語句一次返回一個(gè)結(jié)果,在每個(gè)結(jié)果中間,掛起函數(shù)的狀態(tài),以便下次重它離開   的地方繼續(xù)執(zhí)行

  2.生成器表達(dá)式:類似于列表推導(dǎo),但是,生成器返回按需產(chǎn)生結(jié)果的一個(gè)對(duì)象,而不是一次構(gòu)建一個(gè)結(jié)果列表

?

  生成器Generator:

  本質(zhì):迭代器(所以自帶了__iter__方法和__next__方法,不需要我們?nèi)?shí)現(xiàn))

  特點(diǎn):惰性運(yùn)算,開發(fā)者自定義

?

?

  生成器函數(shù)

  一個(gè)包含yield關(guān)鍵字的函數(shù)就是一個(gè)生成器函數(shù)。yield可以為我們從函數(shù)中返回值,但是yield又不同于return,return的執(zhí)行意味著程序的結(jié)束,調(diào)用生成器函數(shù)不會(huì)得   到返回的具體的值,而是得到一個(gè)可迭代的對(duì)象。每一次獲取這個(gè)可迭代對(duì)象的值,就能推動(dòng)函數(shù)的執(zhí)行,獲取新的返回值。直到函數(shù)執(zhí)行結(jié)束。

import time def genrator_fun1():a = 1print('現(xiàn)在定義了a變量')yield ab = 2print('現(xiàn)在又定義了b變量')yield bg1 = genrator_fun1() print('g1 : ',g1) #打印g1可以發(fā)現(xiàn)g1就是一個(gè)生成器 print('-'*20) #我是華麗的分割線 print(next(g1)) time.sleep(1) #sleep一秒看清執(zhí)行過程 print(next(g1)) 初識(shí)生成器函數(shù)

?

  生成器有什么好處呢?就是不會(huì)一下子在內(nèi)存中生成太多數(shù)據(jù)

  假如我想讓工廠給學(xué)生做校服,生產(chǎn)2000000件衣服,我和工廠一說,工廠應(yīng)該是先答應(yīng)下來,然后再去生產(chǎn),我可以一件一件的要,也可以根據(jù)學(xué)生一批一批的找工?   廠拿。
  而不能是一說要生產(chǎn)2000000件衣服,工廠就先去做生產(chǎn)2000000件衣服,等回來做好了,學(xué)生都畢業(yè)了。。。

#初識(shí)生成器二def produce():"""生產(chǎn)衣服"""for i in range(2000000):yield "生產(chǎn)了第%s件衣服"%iproduct_g = produce() print(product_g.__next__()) #要一件衣服 print(product_g.__next__()) #再要一件衣服 print(product_g.__next__()) #再要一件衣服 num = 0 for i in product_g: #要一批衣服,比如5件print(i)num +=1if num == 5:break#到這里我們找工廠拿了8件衣服,我一共讓我的生產(chǎn)函數(shù)(也就是produce生成器函數(shù))生產(chǎn)2000000件衣服。 #剩下的還有很多衣服,我們可以一直拿,也可以放著等想拿的時(shí)候再拿 初識(shí)生成器二

?

  更多應(yīng)用

import timedef tail(filename):f = open(filename)f.seek(0, 2) #從文件末尾算起while True:line = f.readline() # 讀取文件中新的文本行if not line:time.sleep(0.1)continueyield linetail_g = tail('tmp') for line in tail_g:print(line) 生成器監(jiān)聽文件輸入的例子

send

def generator():print(123)content = yield 1print('=======',content)print(456)yield2g = generator() ret = g.__next__() print('***',ret) ret = g.send('hello') #send的效果和next一樣 print('***',ret)#send 獲取下一個(gè)值的效果和next基本一致 #只是在獲取下一個(gè)值的時(shí)候,給上一yield的位置傳遞一個(gè)數(shù)據(jù) #使用send的注意事項(xiàng)# 第一次使用生成器的時(shí)候 是用next獲取下一個(gè)值# 最后一個(gè)yield不能接受外部的值 def averager():total = 0.0count = 0average = Nonewhile True:term = yield averagetotal += termcount += 1average = total/countg_avg = averager() next(g_avg) print(g_avg.send(10)) print(g_avg.send(30)) print(g_avg.send(5)) 計(jì)算移動(dòng)平均值(1) def init(func): #在調(diào)用被裝飾生成器函數(shù)的時(shí)候首先用next激活生成器def inner(*args,**kwargs):g = func(*args,**kwargs)next(g)return greturn inner@init def averager():total = 0.0count = 0average = Nonewhile True:term = yield averagetotal += termcount += 1average = total/countg_avg = averager() # next(g_avg) 在裝飾器中執(zhí)行了next方法 print(g_avg.send(10)) print(g_avg.send(30)) print(g_avg.send(5)) 計(jì)算移動(dòng)平均值(2)_預(yù)激協(xié)程的裝飾器

  yield from

def gen1():for c in 'AB':yield cfor i in range(3):yield iprint(list(gen1()))def gen2():yield from 'AB'yield from range(3)print(list(gen2())) yield from

?

  列表推導(dǎo)式和生成器表達(dá)式

#老男孩由于峰哥的強(qiáng)勢(shì)加盟很快走上了上市之路,alex思來想去決定下幾個(gè)雞蛋來報(bào)答峰哥 egg_list=['雞蛋%s' %i for i in range(10)] #列表解析#峰哥瞅著alex下的一筐雞蛋,捂住了鼻子,說了句:哥,你還是給我只母雞吧,我自己回家下 laomuji=('雞蛋%s' %i for i in range(10))#生成器表達(dá)式 print(laomuji) print(next(laomuji)) #next本質(zhì)就是調(diào)用__next__ print(laomuji.__next__()) print(next(laomuji)) 峰哥與alex的故事

  總結(jié):

  1.把列表解析的[]換成()得到的就是生成器表達(dá)式

  2.列表解析與生成器表達(dá)式都是一種便利的編程方式,只不過生成器表達(dá)式更節(jié)省內(nèi)存

  3.Python不但使用迭代器協(xié)議,讓for循環(huán)變得更加通用。大部分內(nèi)置函數(shù),也是使用迭代器協(xié)議訪問對(duì)象的。例如, sum函數(shù)是Python的內(nèi)置函數(shù),該函數(shù)使用迭代? ? ? ? ?  器協(xié)議訪問對(duì)象,而生成器實(shí)現(xiàn)了迭代器協(xié)議,所以,我們可以直接這樣計(jì)算一系列值的和:

sum(x ** 2 for x in range(4))

  而不用多此一舉的先構(gòu)造一個(gè)列表:

sum([x ** 2 for x in range(4)])

 總結(jié)

  可迭代對(duì)象:

  擁有__iter__方法

  特點(diǎn):惰性運(yùn)算

  例如:range(),str,list,tuple,dict,set

  迭代器Iterator

  擁有__iter__方法和__next__方法

  例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

  生成器Generator:

  本質(zhì):迭代器,所以擁有__iter__方法和__next__方法

  特點(diǎn):惰性運(yùn)算,開發(fā)者自定義

  使用生成器的優(yōu)點(diǎn):

  1.延遲計(jì)算,一次返回一個(gè)結(jié)果。也就是說,它不會(huì)一次生成所有的結(jié)果,這對(duì)于大數(shù)據(jù)量處理,將會(huì)非常有用。

?

#列表解析 sum([i for i in range(100000000)])#內(nèi)存占用大,機(jī)器容易卡死#生成器表達(dá)式 sum(i for i in range(100000000))#幾乎不占內(nèi)存 列表解析式和生成器表達(dá)式

?

  2.提高代碼可讀性

?

?

 生成器相關(guān)的面試題

  生成器在編程中發(fā)生了很多的作用,善用生成器可以幫助我們解決很多復(fù)雜的問題

  除此之外,生成器也是面試題中的重點(diǎn),在完成一些功能之外,人們也想出了很多魔性的面試題。
  接下來我們就來看一看~

def demo():for i in range(4):yield ig=demo()g1=(i for i in g) g2=(i for i in g1)print(list(g1)) print(list(g2)) 面試題1 def add(n,i):return n+idef test():for i in range(4):yield ig=test() for n in [1,10]:g=(add(n,i) for i in g)print(list(g)) 面試題2 import osdef init(func):def wrapper(*args,**kwargs):g=func(*args,**kwargs)next(g)return greturn wrapper@init def list_files(target):while 1:dir_to_search=yieldfor top_dir,dir,files in os.walk(dir_to_search):for file in files:target.send(os.path.join(top_dir,file)) @init def opener(target):while 1:file=yieldfn=open(file)target.send((file,fn)) @init def cat(target):while 1:file,fn=yieldfor line in fn:target.send((file,line))@init def grep(pattern,target):while 1:file,line=yieldif pattern in line:target.send(file) @init def printer():while 1:file=yieldif file:print(file)g=list_files(opener(cat(grep('python',printer()))))g.send('/test1')協(xié)程應(yīng)用:grep -rl /dir tail&grep

?

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/xiaole-7890/p/9332191.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的python之路——迭代器和生成器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。