Python—迭代器與生成器

迭代器與生成器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

 

StopIteration異常:
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
yield : (返回並暫停)保持當前狀態並中斷,下次運行從這個狀態開始
send(): 給yiield傳值並調用  .__next__() 至關於 .send(None)
next:調用yield,喚醒它
 
作包子練習
 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")
View Code

 

 

迭代器


 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 >>> 
View Code

 

【你可能會問,爲何listdictstr等數據類型不是Iterator

這是由於Python的Iterator對象表示的是一個數據流,Iterator對象能夠被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。能夠把這個數據流看作是一個有序序列,但咱們卻不能提早知道序列的長度,只能不斷經過next()函數實現按需計算下一個數據,因此Iterator的計算是惰性的,只有在須要返回下一個數據時它纔會計算。

Iterator甚至能夠表示一個無限大的數據流,例如全體天然數。而使用list是永遠不可能存儲全體天然數的。】

                                                  —https://www.cnblogs.com/alex3714/articles/5765046.html

相關文章
相關標籤/搜索