python迭代器和生成器(3元運算,列表生成式,生成器表達式,生成器函數)

1.1迭代器

什麼是迭代器:python

迭代器是一個能夠記住遍歷的位置對象算法

迭代器對象從集合的第一個元素元素開始訪問,直到全部元素被訪問完結束,迭代器只能往前不會後退。編程

迭代器有兩個基本方法:iter ,next 方法併發

內置函數iter(),next()  本質上都是用的對象.__iter__(),__next__()的方法ide

內置函數 iter(iterable),表示把可迭代對象 變成迭代器(iterator)函數

內置函數next(iterator) ,表示查看下一次迭代的值(固然也能夠用 iterator.__next__() ,查看下一次迭代的值)工具

1.1.2迭代器協議

1.迭代器(iterator)協議是指:對象必須提供一共next方法,執行該方法妖魔返回迭代中的下一項,要麼就引發一個Stopiteration異常,已終止迭代。spa

2.可迭代對象:實現了迭代器協議的對象,(該對象內部定義了一個__iter__()的方法  例:str.__iter__())就是可迭代對象線程

3.協議是一種約定,可迭代對象實現了迭代器協議,python的內部工具(如。for循環,sum,min,max函數等)使用迭代器協議訪問對象3d

 

1.1.3python中的for循環

for循環本質:循環全部對象,所有是使用迭代器協議

(字符串,列表,元祖,字典,集合,文件對象),這些都不是可迭代對象,只不過在for循環,調用了他們內部的__iter__方法,把他們變成了可迭代對象。而後for循環調用可迭代對象的__next__方法去去找,而後for循環會自動捕捉StopIteration異常,來終止循環。

 1 l1 = ["hello","world",1,2]
 2 
 3 #for循環調用可迭代對象的__next__方法去取值,並且for循環會捕捉StopIteration異常,以終止對象
 4 for i in l1:
 5     print(i)
 6 
 7 aa = l1.__iter__()  #等同於內置函數aa = iter(l1) 建立了一個list_iterator 列表迭代器
 8 print(type(aa))
 9 print(next(aa))   #內置函數 next()查看第一次迭代器的值
10 print(aa.__next__())   #迭代器自己對象的方法,第二次迭代器的值   跟 內置函數方法都是同樣的
11 print(next(aa))
12 print(next(aa))
13 print(next(aa))  #沒有可迭代的值了也就是迭代完了,會報錯:StopIteration
14 
15 
16 #迭代器迭代完,就不能再次迭代該迭代器 好比for 循環
17 for i in aa:
18     print(i)
demo
 1 # 首先得到Iterator對象:
 2 it = iter([1, 2, 3, 4, 5]) #建立一個迭代器
 3 # 循環:
 4 while True:
 5     try:
 6         # 得到下一個值:
 7         x = next(it)
 8         print(x)
 9     except StopIteration:
10         # 遇到StopIteration就退出循環
11         break
demo2 :循環比迭代器更強大

 

總結:

1.可做用於for循環對象自己都是iterable(可迭代對象)類型,或者對象自己有obj.__iter__方法也是iterable

2.凡是可做用於next()函數的對象自己itertor(迭代器)類型,或者obj.__next__也是iterator ,迭代器是一個惰性序列

由於須要調用next,纔會得到元素,迭代完,就不能再次迭代。

3.list、dict、str等是iterable,但不是iterator不過能夠經過iter()函數得到一個迭代器對象。

 

1.2生成器

什麼是生成器?

1.從字面理解是否是:生成一個容器

2.在python中,一邊循環,一邊計算的機制,稱爲生成器(generator)。

3.能夠理解爲一種數據類型,這種類型自動實現了迭代器協議。(其餘的數據類型須要調用自已的內置__iter__方法或則iter()的內置函數),因此生成器就是一個可迭代對象。

 

生成器分類以及在python中的表現形式。(python有兩種不一樣的方式提供生成器)

1.生成器函數:常規函數定義,可是,使用yield語句而不是return語句的返回結果。yield語句一次返回一個結果,在每一個結果中間,保留函數的狀態,以便再上一次狀態的從新執行。

2.生成器表達式:相似於列表推導,可是生成器返回按需產生結果的一種對象,而不是一次構建一個結果列表

 

爲什麼使用生成器,生成器的優勢:

python使用生成器對延遲操做提供了支持。所謂延遲操做,是指須要的時候才產生結果,而不是當即生成結果

這就是生成器的好處

 

生成器小結:

1.生成器是可迭代對象

2.實現了延遲計算,看內存(按需,執行)

3.生成器本質和其餘類型同樣,都是實現了迭代器協議,只不過生成器是一邊計算,一邊生成,從而節省內存空間,

其他的可迭代對象可沒有好處。

 

定義生成器的前提:

1.考慮這個生成器是否須要屢次遍歷。

2.這個生成器內存空間的問題。

3.時間效率問題。

 

生成器是一個惰性的,根據惰性求值:也就是須要一個對象給一個對象

 

1.2.1生成器表達式、列表生成式、三元表達式

1.三元運算或則3元表達式

 1 #三元表達式格式
 2 
 3 res=值1  if  條件  else  值2 
 4 
 5 #若是條件知足 res 等於 值1  條件不知足就等於 值2
 6 # demo 1
 7 name = "xixi"
 8 res = "xixi"  if name == "xixi" else "hello"
 9 print(res)
10 
11 #demo 2
12 num = 2 if False else 0
13 print(num)

2.列表生成式

 1 #列表生成式經過計算生成一個列表
 2 
 3 lis_gen = [ i for i in range(1,10)]  #列表生成式
 4 print(lis_gen)
 5 
 6 lis1_gen = [i for i in range(1,10) if i%2 == 0]  #生成一個偶數的列表
 7 print(lis1_gen)
 8 
 9 lis2_gen = [ i * i for  i in range(1,10) if i%2 == 1]  #生成以個奇數乘自已自己奇數的列表
10 print(lis2_gen)
列表生成式[] demo

3.生成器表達式

1 gen_exp = (i for i in range(10))  #生成器表達式
2 print(gen_exp) #generator
3 # for i in gen_exp:  #取出生成器表達式的值,for循環
4 #     print(i)
5 print(gen_exp.__next__()) #next方法
6 print(gen_exp.__next__())
7 print(gen_exp.__next__())
8 print(gen_exp.__next__())
9 print(gen_exp.__next__())
生成器表達式 () demo
1 gen = (i for i in range(10**100))  #生成器表達式
2 lis = [i for i in range(10**100)]  #列表生成式
3 
4 #生成器,更省內存,須要一個取一個
5 print(gen.__next__())
6 print(lis)  #須要在內存空間建立1-10**100序列
生成器表達式和列表生成式比較 ()和[]

 總結:

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

2.列表生成式式一個構建一個結果列表,生成器表達式:是返回按需產生結果的一個對象

3.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存

4.python不但使用迭代器協議讓for循環更加通用,大部份內置函數,也是使用迭代器協議訪問對象的

如,sum函數是python的內置函數,該函數使用迭代器協議訪問對象,而生成器實現了迭代器協議

 

1.2.2生成器函數

 在python中,使用了yield的函數就稱爲生成器(generator)

1.跟普通函數不一樣的是,生成器是一個返回迭代器的函數,只能用於迭代操做,能夠理解爲:生成器就是一個迭代器

2.在調用生成器運行過程當中,每次遇到yield是函數會暫停並保存當前全部的運行信息,返回yield值。並在下一次執行next方法時,從當前位置繼續運行。

普通生成器:

 1 >>> gen = (i for i in range(5))
 2 >>> gen
 3 <generator object <genexpr> at 0x0000004DE29A70A0>
 4 >>> next(gen)
 5 0
 6 >>> next(gen)
 7 1
 8 >>> next(gen)
 9 2
10 >>> next(gen)
11 3
12 >>> next(gen)
13 4
14 >>> next(gen)
15 Traceback (most recent call last):
16   File "<stdin>", line 1, in <module>
17 StopIteration
算法 實現生成器

注:generator保存的是算法,每次調用next方法,就計算出gen的下一個元素的值,直到計算到最後一個元素,沒有更多元素時,就StopIteration的錯誤。

固然,上面這種不斷調用next(gen),用着有點坑,正確的方法是使用for循環,由於generator也是iterator;

1 >>> g = (i for i in range(5))
2 >>> for i in g:
3 ...     print(i)
4 ...
5 0
6 1
7 2
8 3
9 4
for generator

因此咱們建立了一個generator後,基本不會調用next方法,而是經過for循環來迭代它,而且不是關心StopIteration的錯誤。

generator很是強大,若是計算的算法比較複雜,用for循環沒法實現的時候,還能夠用函數來實現。

例:斐波拉契數列 後面的一個數等於前面兩個數相加的和

1 def fib(number):
2     #得出幾個斐波拉契數列
3     count,a,b = 0,0,1
4     while count < number:
5         print(b)
6         a,b = b,a+b
7         count += 1
8     return "done"
9 fib(5)
斐波拉契數列,普通函數定義
 1 def fib1(number):
 2     n,a,b = 0,0,1
 3     while n<number:
 4         yield b
 5         a,b = b,a+b
 6         n += 1
 7     return "done"
 8 aa = fib1(6)
 9 print(aa)  #generator
10 # print(aa.__next__())
11 for i in aa:
12     print(i)
斐波拉契數列,yield函數 定義

注:若是一個函數定義中包含yield關鍵字,那麼這個函數就不是普通函數,而是一個generator

注:generator和函數執行的流程不同,

函數是順序執行,遇到return語句或則最後一行函數函數語句就返回。

而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行從上次返回的yield語句處繼續執行

 

 1 def packet():
 2     for i in range(1,10):
 3         print("開始生產包子")
 4         yield  "第 %d 屜包子" %(i)
 5         print("賣包子,買完再生產")
 6 cs = packet()  #生成一個作包子的生成器,至關於作包子的
 7 # print(cs)
 8 q = print(cs.__next__()) #賣包子的
 9 print(cs.__next__())
10 for i in cs:
11     print(i)
生產 ,賣的過程
 1 #單線程一邊發送,一邊執行
 2 import time
 3 def consumer(name):
 4     print("%s 準備吃包子啦!" %name)
 5     while True:
 6        baozi = yield
 7 
 8        print("包子[%s]來了,被[%s]吃了!" %(baozi,name))
 9 def producer(name):
10     c = consumer('A')
11     c2 = consumer('B')
12     c.__next__()
13     c2.__next__()
14     print("老子開始準備作包子啦!")
15     for i in range(10):
16         time.sleep(1)
17         print("作了2個包子!")
18         c.send(i) #發送的值,就是yield的返回值
19         c2.send(i)
20 producer("xixi")
yield生成器,單線程併發

1.2.3生成器函數總結

1.生成器函數語法上和普通函數相似:生成器使用yield語句返回一個值,而常規函數使用return語句返回一個值

2.生成器自動實現迭代器協議,迭代完,就不能再次迭代。

3.狀態掛起:生成器使用yield語句返回一個值。掛起該生成器函數的狀態。

相關文章
相關標籤/搜索