迭代器與生成器html
生成器(generator)算法
先來了解一下列表生成器:yii
1 list = [i*2 for i in range(10)] 2 print(list)
>>>>
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。ide
因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器:generator。函數
要建立一個generator,有不少種方法。第一種方法很簡單,只要把一個列表生成式的[]改爲(),就建立了一個generator:spa
gen1 = (i*2 for i in range(10)) print(gen1) >>>>> <generator object <genexpr> at 0x0000022419B21AF0> #列表生成器打印出來只是一個內存地址
要注意的是:code
1.打印生成器,只是打印其內存地址,生成器只有在調用的時候,纔會產生元素,只能一個個取值htm
2.生成器不能像列表同樣訪問某個元素,或者切片。只能經過for循環打印出來,或者經過 【__next__()】,括號裏不能給參數 2.7裏 是 next()對象
3.生成器只有一個 __next__() 方法,生成器只會記住當前的取值,能夠用next方法調用下一個,可是不能往前,內置函數 next也能夠調用,for循環也能夠調用,還能夠數據類型強制轉換: list(generator)blog
>在一次運行過程當中,生成器遍歷取值完就沒有值了。 迭代器也是遍歷完就沒有值了,一樣也會有 StopIteration Error
>生成器是一類特殊的迭代器。在函數中用 yield
#for循環調用生成器,,yield不能和return共用,且要寫在函數內部
>>> def generator(): print(1) yield 33333 #能夠把yield當作return一個值,可是不結束函數,只是暫時中斷 print(2) yield 44444 >>> g = generator() #此時g就是一個生成器,generator就是iterator,因此能夠用for循環 >>> for i in g: print(i) ----> 1 33333 2 44444 >>>
用函數生成生成器,以斐波那契數列進行舉例(yield不能和return共用,且要寫在函數內部)
1 def fibo(max): 2 n,a,b = 0,0,1 3 while n<max: 4 #print(b) 5 yield b #生成器的建立 yield:(返回後暫停)保持當前狀態並中斷函數,下次運行時,從這裏日後運行,由於保存了當前狀態 6 a,b = b,a+b #至關於 t =(b,a+b) a = t[0] b = [t1] 7 n +=1 8 return 'done' 9 10 f = fibo(10) 11 print(f.__next__()) 12 print(f.__next__()) 13 print('----作點別的事情----') #生成器能夠調用一下,而後停下來作別的事,其餘函數會一口氣打印出全部結果 14 print(f.__next__()) 15 print(f.__next__()) 16 print('----開始for循環----') #__next__方法只記錄當前位置 17 for i in f: 18 print(i) 19 20 >>>>>#結果以下所示 21 1 22 1 23 ----作點別的事情---- 24 2 25 3 26 ----開始for循環---- 27 5 28 8 29 13 30 21 31 34 32 55
def fibo(max): n,a,b = 0,0,1 while n<max: #print(b) yield b a,b = b,a+b #至關於 t =(b,a+b) a = t[0] b = [t1] n +=1 return '----done----' f = fibo(3) #只運行3次斐波那契數列 print(f.__next__()) print(f.__next__()) print(f.__next__()) print(f.__next__()) #此時調用了4次next方法 ,此時會報 StopIteration錯誤
>>>>>> Traceback (most recent call last): 1 File "C:/Users/15302/PycharmProjects/GKXXX/day3/斐波那契數列.py", line 20, in <module> 1 print(f.__next__()) 2 StopIteration: ----done---- #這個done是函數返回值
能夠用【try—except】來抓住異常(for循環用的就是這種機制)
def fibo(max): ‘--snip--’ f = fibo(5) while True: #用 try—except 來抓住異常 try: x = next(f) print('斐波那契數列:',x) #打印每次運行generator的值 except StopIteration as e: #抓住StopIteration異常 print('Generator return value:',e.value) #輸出返回值 break >>>>> 斐波那契數列: 1 斐波那契數列: 1 斐波那契數列: 2 斐波那契數列: 3 斐波那契數列: 5 Generator return value: ----done----
關於yield(yield相似return,若是不打印是不顯示yield的值的,只會執行程序)
def gen(): print('start') m = yield 2 # 能夠看做yield返回值爲2,send(3)把3傳遞給m,並調用,m變成3了,同下 print(m) n = yield 3 print(n) try: g = gen() #此時不運行gen()函數,若print(g) 會打印該生成器的內存地址 g.send(None) # 至關於 g.__next__() 此處若 print(g.send(None)) 則先執行 打印start,而後執行 打印 yiled的返回值 2,而後函數暫停 g.send(3333) #此處若 print(g.send(3333)) 首先函數繼續往下走,先把send裏的3333賦值給m,而後打印m,接下來執行 yield 3的返回值,打印3 g.send(6666) except StopIteration as e: print(e.value) >>>> start 3333 6666 None
1 import time 2 def consumer(name): 3 print("%s 準備吃包子啦!" %name) 4 while True: 5 baozi = yield 6 7 print("包子[%s]來了,被[%s]吃了!" %(baozi,name)) 8 9 # c = consumer('gkx') #此時不運行程序,即當函數中有yield時候,必定要用 __next__,send()方法調用纔會運行,這句話至關於把函數變成生成器而已 10 # c.__next__() #第一次運行到 yield,而後保存當前狀態,中止 11 # b1 = 'jiucai' 12 # c.send(b1) 13 #c.__next__() #運行yield日後的語句,即第二句 print處 14 def producer(name): 15 c = consumer('A') 16 c2 = consumer('B') 17 c.__next__() 18 c2.__next__() 19 print("開始準備作包子啦!") 20 for i in range(10): 21 time.sleep(1) 22 print("作了2個包子!") 23 c.send(i) 24 c2.send(i) 25 26 producer("gkx")
迭代器
1.凡是可做用於for循環的對象都是Iterable類型; 可迭代協議— 只要含有 __iter__方法的都是可迭代的
1 >>> from collections import Iterable 2 >>> isinstance([],Iterable) 3 True 4 >>> isinstance((),Iterable) 5 True 6 >>> isinstance({},Iterable) 7 True 8 >>> isinstance(‘abc’,Iterable) 9 True 10 >>> isinstance((x for x in range(10)),Iterable) 11 True 12 >>> isinstance(100,Iterable) 13 False 14 15 #list,set,dict,str,generatior都是可迭代的,數字不可迭代
print('__iter__' in dir([])) >>>>>簡單粗暴的判斷方法
2.凡是可做用於next()函數的對象都是Iterator(迭代器)類型,它們表示一個惰性計算的序列;(生成器必定是迭代器,迭代器不必定是生成器)
迭代器協議:含有 __next__ 和 __iter__方法的,就是迭代器
1 >>> from collections import Iterator 2 >>> isinstance((x for x in range(10)),Iterator) 3 True 4 >>> isinstance([],Iterator) 5 False 6 >>> isinstance({},Iterator) 7 False 8 >>> isinstance('abc',Iterator) 9 False 10 >>> 11 12 #在dict,set,list,str,generator中,只有generator纔是迭代器
3.集合數據類型如list、dict、str等是Iterable但不是Iterator,不過能夠經過iter()函數得到一個Iterator對象。
from collections import Iterator
1 >>> isinstance(iter([]),Iterator) 2 True 3 >>> isinstance(iter({}),Iterator) 4 True
>>> a = ['1','2']
>>> iter(a)
<list_iterator object at 0x00000262D6E85F60>
>>> iter(a).__next__() #把列表a轉換爲迭代器,可使用__next__()函數
'1'
>>>
咱們在文件操做中,用 for line in f: 其中 文件句柄 f 就是迭代器
for循環等價於:
1 >>> it = iter(range(10)) 2 >>> while True: 3 try: 4 x = next(it) 5 print(x) 6 except StopIteration: 7 break 8 9 10 0 11 1 12 2 13 3 14 4 15 5 16 6 17 7 18 8 19 9 20 >>>
【你可能會問,爲何list
、dict
、str
等數據類型不是Iterator
?
這是由於Python的Iterator
對象表示的是一個數據流,Iterator對象能夠被next()
函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration
錯誤。能夠把這個數據流看作是一個有序序列,但咱們卻不能提早知道序列的長度,只能不斷經過next()
函數實現按需計算下一個數據,因此Iterator
的計算是惰性的,只有在須要返回下一個數據時它纔會計算。
Iterator
甚至能夠表示一個無限大的數據流,例如全體天然數。而使用list是永遠不可能存儲全體天然數的。】
—https://www.cnblogs.com/alex3714/articles/5765046.html