Python知識點-生成器和迭代器
阿新 • • 發佈:2018-12-18
1.先了解下列表生成式
有一個列表[1,2,3,4,5,6,7,8,9],對沒一個元素都進行*2
#通用做法是用lamba ret = map(lambda x:x*2,[1,2,3,4,5,6,7,8,9]) #python3 得到的ret 是一個map物件,在通過for迴圈得到 #python2 得到的事一個列表不需要再次for迴圈 print(ret) #<map object at 0x104ef66d8> for i in ret: print(i) #通過列表生成式直接生成一個列表 ret = [x*2 for x in [1,2,3,4,5,6,7,8,9]]
2.什麼是生成器?
通過列表生成式,我們可以直接建立一個列表,但是,受到記憶體限制,列表容量肯定是有限的,而且建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,就會浪費大量的記憶體空間。
所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間,在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator
生成器是一個特殊的程式,可以被用作控制迴圈的迭代行為,python中生成器是迭代器的一種,使用yield返回值函式,每次呼叫yield會暫停,而可以使用next()函式和send()函式恢復生成器。
3.python中的生成器,生成器表示式:返回一個物件,這個物件只有在需要的時候才產生結果
#要建立一個generator,有很多種方法,第一種方法很簡單,只有把一個列表生成式的[]中括號改為()小括號,就建立一個generator #列表生成式 lis = [x*x for x in range(10)] print(lis) #生成器 generator_ex = (x*x for x in range(10)) print(generator_ex) 結果: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <generator object <genexpr> at 0x000002A4CBF9EBA0> #得到的結果也不一樣,一個是列表一個是物件,那麼如何打印出generator_ex的每一個元素呢?通過generator_ex.__next__()或者next(generator_ex) 結果: 0 1 4 9 16 25 36 49 64 81 Traceback (most recent call last): File "列表生成式.py", line 42, in <module> print(next(generator_ex)) StopIteration #到最後一個元素後,再次呼叫__next__() 會觸發 StopIteration error,代表已經沒有元素了,這種感覺很不好,所以一般我們使用for 迴圈拿就不會觸發error,因為generator也是可迭代物件 for i in generator_ex: print(i)
3.生成器函式使用以斐波那切數列舉例,從第3項開始,每一項都等於前兩項之和。1 1 2 3 5 8 13 21 34......
生成器函式:也是用def定義的,利用關鍵字yield一次性返回一個結果,阻塞,重新開始
#斐波那契數列,普通函式形式 def fib(number): n=0 a,b=0,1 while n<number: a,b=b,a+b n=n+1 print(a) return ('done') a = fib(10) print(a) # 通過生成器實現,每次呼叫__next__() 都會yield d 拿到b 這個值,然後繼續迴圈到yiled b停住,繼續監聽有沒有next呼叫 def fib(number): n=0 a,b=0,1 while n <number: yield b a,b=b,a+b n=n+1 return('done') f=fib(3) # 通過for迴圈拿不到return值 for i in f: print(i) #結果: 1 1 2 3 5 8 13 21 34 55 #要是想得到return 返回值就需要__next__()通過try except獲取異常 def fib(number): n,a,b =0,0,1 while n < number: yield b a,b =b,a+b n = n+1 return('done') g = fib(6) while True: try: x = __next__(g) print('generator: ',x) except StopIteration as e: print("生成器返回值:",e.value) break 結果: generator: 1 generator: 1 generator: 2 generator: 3 generator: 5 generator: 8 生成器返回值: done
4. 生成器 send 用法
def generator(): while True: receive = yield 1 print('hahaha ' + str(receive)) yield 2 g = generator() print(next(g)) #一定要先next一次後再send值,不然會報錯 print(g.send('ok')) #send ok 給receive接收,receive = yield 1 這句就代表結束了,然後遇到下一次的yiled斷點 返回yiled的值 # print(next(g)) #結果: 1 hahaha ok 2 1
5.通過 generator yiled 來實現協程,標準的生產者-消費者模型
import time def consumer(): r = '' while True: r = '我餓了' n = yield r #這個n 是生產者傳送過來的n 不是r 這個值 print(n) if not n: return print('[消費者] 消費了 %s 個大包子...' % n) # time.sleep(1) # c = consumer() # print(c.__next__()) # print(c.send('a')) def produce(c): # 消費者呼叫next 準備send包子 print(c.__next__()) n = 0 #設定生產幾個包子 while n < 5: n = n + 1 print('[生產者] 生產了 %s 個大包子...' % n) r = c.send(n) #send給 消費者 並且接收了yiled '我餓了' if r == '我餓了': print('[生產者] 消費者說真他媽好吃啊') else: print('[生產者] 消費者說狗都不想吃') c.close() if __name__=='__main__': c = consumer() #消費者例項 produce(c) #結果: 我餓了 [生產者] 生產了 1 個大包子... [消費者] 消費了 1 個大包子... [生產者] 消費者說真他媽好吃啊 [生產者] 生產了 2 個大包子... [消費者] 消費了 2 個大包子... [生產者] 消費者說真他媽好吃啊 [生產者] 生產了 3 個大包子... [消費者] 消費了 3 個大包子... [生產者] 消費者說真他媽好吃啊 [生產者] 生產了 4 個大包子... [消費者] 消費了 4 個大包子... [生產者] 消費者說真他媽好吃啊 [生產者] 生產了 5 個大包子... [消費者] 消費了 5 個大包子... [生產者] 消費者說真他媽好吃啊
6.迭代器
迭代器就是迴圈,並且可以用next呼叫
可迭代物件Iterable不一定是Iterator迭代器,但是迭代器一定時可迭代物件。
生成器都是迭代器Iterator,但list、dict、str雖然是Iterable(可迭代物件),卻不是Iterator(迭代器),可以使用for 迴圈的都是可迭代物件。
#判斷是否為可迭代物件 >>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False #也可以使用isinstance()判斷一個物件是否是迭代器Iterator物件: >>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False #可以使用iter()變成迭代器物件 >>> isinstance(iter([]), Iterator) True >>> isinstance(iter('abc'), Iterator) True >>> a=[1,2,3,4] >>> b=iter(a) >>> print(b) <list_iterator object at 0x103c7e6d8> >>> b.__next__() 1 >>> b.__next__() 2 >>> b.__next__() 3 >>> b.__next__() #要判斷一個物件是不是迭代器 可以直接用next方法呼叫,或者用isinstance(物件,Iterator)