day16 叠代器和生成器
分頁查找
#5.隨意寫一個20行以上的文件(divmod) # 運行程序,先將內容讀到內存中,用列表存儲。 # l = [] # 提示:一共有多少頁 # 接收用戶輸入頁碼,每頁5條,僅輸出當頁的內容 def read_page(bk_list,n,endline=None): startline = 5 * (n-1) endline = endline+startline-1 if endline else startline + 4 # print(startline,endline) for i in range(startline,endline+1):print(bk_list[i],end = ‘‘) def read_book(filename): f = open(filename) book_list = f.readlines() f.close() return book_list book_list = read_book(‘tmp_file‘) line_num = len(book_list) x,y = divmod(line_num,5) print(line_num,x,y) # if y: # page = x+1 # else: # page = x page = x+1 ify else x print(‘一共有%s頁‘%page) while True: page_num = input(‘請輸入您要閱讀的頁碼 : ‘).strip() if page_num.isdigit(): page_num = int(page_num) if page_num < page: read_page(book_list,page_num) elif page_num == page: read_page(book_list,page_num,y)else: print(‘您輸入的內容有誤‘)
一、可叠代對象
可以將某個數據集內的數據“一個挨著一個的取出來”,就叫做叠代。
可叠代協議
假如我們自己寫了一個數據類型,希望這個數據類型裏的東西也可以使用for被一個一個的取出來,那我們就必須滿足for的要求。這個要求就叫做“協議”。
可以被叠代要滿足的要求就叫做可叠代協議。可叠代協議的定義非常簡單,就是內部實現了__iter__方法。
可以被for循環的都是可叠代的,要想可叠代,內部必須有一個__iter__方法。
l = [1,2,3,4,5] s = {1,2,3,4} #索引 #for # for i in l: # print(i) # for i in 50: # print(i) #iterable 可叠代的 #叠代 #str #列表 #tuple #set #dict #可叠代的 ——對應的標誌 __iter__ # print(‘__iter__‘ in dir([1,2,3])) #判斷一個變量是不是一個可叠代的
二、叠代器
接著分析,__iter__方法做了什麽事情呢?
print([1,2].__iter__()) 結果 <list_iterator object at 0x1024784a8>
#可叠代協議 l = [1,2,3,4,5] # for i in l: # print(i) # print(iter(l)) #內置函數 l.__iter__() #iterator #叠代器 #iterator iterable l_iterator = iter(l) # print(set(dir(l_iterator))-set(dir(l))) #__next__ # iter({1,2,3})
叠代器協議
既什麽叫“可叠代”之後,又一個歷史新難題,什麽叫“叠代器”?
雖然我們不知道什麽叫叠代器,但是我們現在已經有一個叠代器了,這個叠代器是一個列表的叠代器。
我們來看看這個列表的叠代器比起列表來說實現了哪些新方法,這樣就能揭開叠代器的神秘面紗了吧?
可叠代協議——凡是可叠代的內部都有一個__iter__方法
叠代器裏既有iter方法,又有next方法 ——叠代器協議
通過iter(o)得到的結果就是一個叠代器,
‘‘‘ dir([1,2].__iter__())是列表叠代器中實現的所有方法,dir([1,2])是列表中實現的所有方法,都是以列表的形式返回給我們的,為了看的更清楚,我們分別把他們轉換成集合, 然後取差集。 ‘‘‘ #print(dir([1,2].__iter__())) #print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2]))) 結果: {‘__length_hint__‘, ‘__next__‘, ‘__setstate__‘}
我們看到在列表叠代器中多了三個方法,那麽這三個方法都分別做了什麽事呢?
iter_l = [1,2,3,4,5,6].__iter__() #獲取叠代器中元素的長度 print(iter_l.__length_hint__()) #根據索引值指定從哪裏開始叠代 print(‘*‘,iter_l.__setstate__(4)) #一個一個的取值 print(‘**‘,iter_l.__next__()) print(‘***‘,iter_l.__next__())
在for循環中,就是在內部調用了__next__方法才能取到一個一個的值。
那接下來我們就用叠代器的next方法來寫一個不依賴for的遍歷。
l=[1,2,8,1,9,4] l_iterator=iter(l) print(l_iterator.__next__()) print(l_iterator.__next__()) while True: try: print(next(l_iterator)) except: break
判斷是否是叠代器 和 可叠代對象的簡便方法
#叠代器 大部分都是在python的內部去使用的,我們直接拿來用就行了 #叠代器:內置__iter__和__next__方法 from collections import Iterable from collections import Iterator #判斷是否是叠代器 和 可叠代對象的簡便方法 # s = ‘abc‘ # print(isinstance(s,Iterable)) # print(isinstance(s,Iterator)) # print(isinstance(iter(s),Iterator)) map_o = map(abs,[1,2,-3,4]) #map_o = [1,2,3,4] print(isinstance(map_o,Iterable)) print(isinstance(map_o,Iterator))
range()
#不管是一個叠代器還是一個可叠代對象,都可以使用for循環遍歷 #叠代器出現的原因 幫你節省內存 from collections import Iterable from collections import Iterator a = range(100) print(isinstance(a,Iterable)) print(isinstance(a,Iterator))
為什麽要有for循環
基於上面講的列表這一大堆遍歷方式,聰明的你立馬看除了端倪,於是你不知死活大聲喊道,你這不逗我玩呢麽,有了下標的訪問方式,我可以這樣遍歷一個列表啊
l=[1,2,3] index=0 while index < len(l): print(l[index]) index+=1 #要毛線for循環,要毛線可叠代,要毛線叠代器
for循環就是基於叠代器協議提供了一個統一的可以遍歷所有對象的方法,即在遍歷之前,先調用對象的__iter__方法將其轉換成一個叠代器,然後使用叠代器協議去實現循環訪問,這樣所有的對象就都可以通過for循環來遍歷了,
初識生成器
我們知道的叠代器有兩種:一種是調用方法直接返回的,一種是可叠代對象通過執行iter方法得到的,叠代器有的好處是可以節省內存。
如果在某些情況下,我們也需要節省內存,就只能自己寫。我們自己寫的這個能實現叠代器功能的東西就叫生成器。
Python中提供的生成器:
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
生成器Generator:
本質:叠代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)
特點:惰性運算,開發者自定義
生成器函數
一個包含yield關鍵字的函數就是一個生成器函數。yield可以為我們從函數中返回值,但是yield又不同於return,return的執行意味著程序的結束,調用生成器函數不會得到返回的具體的值,而是得到一個可叠代的對象。每一次獲取這個可叠代對象的值,就能推動函數的執行,獲取新的返回值。直到函數執行結束。
import time def genrator_fun1(): a = 1 print(‘現在定義了a變量‘) yield a b = 2 print(‘現在又定義了b變量‘) yield b g1 = genrator_fun1() print(‘g1 : ‘,g1) #打印g1可以發現g1就是一個生成器 print(‘-‘*20) #我是華麗的分割線 print(next(g1)) time.sleep(1) #sleep一秒看清執行過程 print(next(g1))
生成器有什麽好處呢?就是不會一下子在內存中生成太多數據
假如我想讓工廠給學生做校服,生產2000000件衣服,我和工廠一說,工廠應該是先答應下來,然後再去生產,我可以一件一件的要,也可以根據學生一批一批的找工廠拿。
而不能是一說要生產2000000件衣服,工廠就先去做生產2000000件衣服,等回來做好了,學生都畢業了。。。
初識生成器二 def produce(): """生產衣服""" for i in range(2000000): yield "生產了第%s件衣服"%i product_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 +=1 if num == 5: break #到這裏我們找工廠拿了8件衣服,我一共讓我的生產函數(也就是produce生成器函數)生產2000000件衣服。 #剩下的還有很多衣服,我們可以一直拿,也可以放著等想拿的時候再拿
讀取文件
import time def tail(filename): f = open(filename) f.seek(0, 2) #從文件末尾算起 while True: line = f.readline() # 讀取文件中新的文本行 if not line: time.sleep(0.1) continue yield line tail_g = tail(‘tmp‘) for line in tail_g: print(line)
計算移動平均值‘
#計算移動平均值 #7日平均年化收益 #10,12,11 = (10+12+11)/3 #total = 10 + 12 + 11 #day = 1 + 1 + 1 #avg = 10/1 22/2 33/3 # 10 11 11 def averager(): total = 0 day = 0 avrage = 0 while True: day_num = yield avrage #return avrage total += day_num day += 1 avrage = total/day # avg = averager() # num = next(avg) #激活生成器 avg.send(),什麽都不send和next效果一樣 # print(avg.send(10)) #傳值 next # print(avg.send(20))
__author__ = ‘Administrator‘ def wapper(fun): def inner(*args,**kwargs): g=fun(*args,**kwargs) next(g) return g return inner @wapper def aa(): day=0 total=0 avrage =0 while True: day_num=yield avrage total+=day_num day+=1 avrage=total/day g=aa() print(g.send(20))
yield from
# __author__ = ‘Administrator‘ # def fun(): # for i in "ab": # yield i # print("....") # yield 1 # # aa=fun() # while True: # try: # print(next(aa)) # except: # break # print(next(aa)) def fun(): yield from "abc" yield from [1,2,4,5] aa=fun() print(next(aa)) print(aa.__next__()) print(next(aa)) print(aa.__next__()) while True: try: print(next(aa)) except: break
列表推導式和生成器表達式
#老男孩由於峰哥的強勢加盟很快走上了上市之路,alex思來想去決定下幾個雞蛋來報答峰哥 egg_list=[‘雞蛋%s‘ %i for i in range(10)] #列表解析 #峰哥瞅著alex下的一筐雞蛋,捂住了鼻子,說了句:哥,你還是給我只母雞吧,我自己回家下 laomuji=(‘雞蛋%s‘ %i for i in range(10))#生成器表達式 print(laomuji) print(next(laomuji)) #next本質就是調用__next__ print(laomuji.__next__()) print(next(laomuji))
# for i in range(100): # print(i*i) # l =[i*i for i in range(100)] # print(l) # l = [{‘name‘:‘v‘,‘age‘:28},{‘name‘:‘v‘}] # name_list = [dic[‘name‘] for dic in l] # print(name_list) # l = [{‘name‘:‘v1‘,‘age‘:28},{‘name‘:‘v2‘}] # name_list_generator = (dic[‘name‘] for dic in l) # print(name_list_generator) # print(next(name_list_generator)) # print(next(name_list_generator)) # egg_list=[‘雞蛋%s‘ %i for i in range(10)] # print(egg_list) # laomuji = (‘雞蛋%s‘ %i for i in range(1,11)) # print(laomuji) # print(next(laomuji)) # print(next(laomuji)) # print(sum([1,2,3])) # print(sum(range(1,4))) def func(): # yield from ‘ABC‘ for i in ‘ABC‘: yield i # g = func() # print(next(g)) # print(next(g)) for i in range(10): print(i) [i for i in range(10)] a = 10 b=20 if a >b : print(a) else: print(b) a if a>b else b
day16 叠代器和生成器