1. 程式人生 > >python迭代器,生成器,裝飾器

python迭代器,生成器,裝飾器

 

1.1迭代器

什麼是迭代器:

迭代器是一個可以記住遍歷的位置物件

迭代器物件從集合的第一個元素元素開始訪問,直到所有元素被訪問完結束,迭代器只能往前不會後退。

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

內建函式iter(),next()  本質上都是用的物件.__iter__(),__next__()的方法

內建函式 iter(iterable),表示把可迭代物件 變成迭代器(iterator)

內建函式next(iterator) ,表示檢視下一次迭代的值(當然也可以用 iterator.__next__() ,檢視下一次迭代的值)

1.1.2迭代器協議

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

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

3.協議是一種約定,可迭代物件實現了迭代器協議,python的內部工具(如。for迴圈,sum,min,max函式等)使用迭代器協議訪問物件

 

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語句返回一個值。掛起該生成器函式的狀態。

 

1.3裝飾器(decorator)

什麼是裝飾器:

器即函式

1.裝飾器可以理解為給一個函式,做修飾,而不修改函式本身。

2.裝飾器定義:本質就是函式,decorator功能就是為其他函式新增新的功能。

裝飾器的的原則

裝飾器=高階函式+函式巢狀+閉包

裝飾器的前提原則:不能修改被裝飾函式的原始碼,和函式的呼叫方式

回到頂部

 1.1.1高階函式

高階函式定義:

1.函式接收的引數是一個函式名。

2.函式的返回值是一個函式。

3.滿足上述條件任意一個,都是高階函式

回到頂部

1.1.2函式巢狀

函式巢狀

python語言中的巢狀:定義一個函式的時候,函式體還能定義另一個函式。

在其他語言(例c語言),在一個函式呼叫另一個函式,叫巢狀

回到頂部

1.1.3閉包

儲存在子封閉作用域(函式)的行為叫做閉包

一個閉包就是一個函式,只不過函式內部帶上了一個額外的變數。

閉包關鍵特點就是它會記住自已被定義時的環境

 

1.2一步一步理解裝飾器

 

1 def go():
2     print("python")
3 #該函式功能執行go() ,顯示出python
4 go()

現在我們要增強go()這個自定義函式的功能,比如在函式呼叫前自動打印出python是什麼樣的語言,但有不想在修改go()函式,這種在程式碼執行期間動態為其新增功能,就稱之為裝飾器(decorator)。

 

 1.現在給go()函式加功能

 1 #一個裝飾器的基本框架
 2 def deco(func):
 3     def wrapper():
 4         func()    #執行你傳的go的函式
 5         print("一個高階語言")
 6     return wrapper
 7 def go():
 8     print("python")
 9 go = deco(go)  #得到結果wrappe的函式r
10 go()  # 執行wrapper函式  ——執行函式func() 函式也就是的go()函式
11 #結果
12  python
13  一個高階語言
14 
15 #上面就是一個裝飾器功能的基本體現,沒有修改go函式的原始碼,也沒有修改go()函式的執行方式,也給go函式加上了一個新功能(高階語言) ,
但是上面每次執行,都需要做一個函式賦值操作,才能執行go() ,這是不完美的

本質上:裝飾器(decrator)就是一個返回函式的高階函式,上面的deco,就是一個裝飾器,接收函式做引數,並返回一個函式,需要藉助python的@語法,@裝飾器

 1 def deco(func):
 2     def wrapper():
 3         func()
 4         print("一個高階語言")
 5     return wrapper
 6 @deco
 7 def go():
 8     print("python")
 9 go()
10 #結果
11 python
12 一個高階語言
13 
14 #現在才相當於一個合格的裝飾器
15 
16 把@deco放到go()函式的定義處,相當於執行了go=deco(go)

上面deco()是一個裝飾器,返回一個函式,所以原來的go()函式仍然存在,

只是現在同名的go變數指向了新的函式,於是呼叫go()函式將執行新函式,

即在go()函式中返回的wrapper()函式

 

2.給被裝飾器函式加引數和返回值

 1 def deco(func):
 2     def wrapper(*args,**kwargs):
 3         res = func(*args,**kwargs)  #這裡就相當於閉包
 4         print("一門高階語言")
 5         return res
 6     return wrapper
 7 
 8 @deco
 9 def go(x,y):
10     print("python",x,y)
11     return "done"
12 go(3,5)
13 # 結果
14 python
15 一個高階語言
16 
17 # 為什麼要給裝飾器加引數,如果被裝飾的函式裡面有引數,我們的裝飾器是不是器也要加相應的引數,
18 我們的裝飾器為什麼要給wrapper(*args,**kwargs) func(*args,**kwargs),可接收任意引數,因為我們被裝飾的函式可能都是不同的的引數,而這個裝飾器,需要給很多函式做裝飾,但是很多函式的引數,功能都是不一樣的,因此我們定義裝飾器 的函式引數應該是加可變長引數
19 
20 #為什麼給裝飾裡面加返回值
21 我們被裝飾的函式,一般是有返回值,而執行裝飾器(@decorator)  所以需要給wrapper 加上返回值來return fun()的執行結果,來保持被裝飾的函式的一致性。
給被裝飾函式加引數和返回值

 

3.給裝飾器加引數

 如果裝飾器本身需要傳入引數,那就需要編寫一個返回裝飾器的高階函式,也就是在原來裝飾器上,做閉包處理,在加上一層函式。

 1 def auth_book(auth=None):
 2     print(auth)
 3     def deco(func):
 4         def wrapper(*args,**kwargs):
 5             res = func(*args,**kwargs)  #這裡就相當於閉包
 6             print("一門高階語言")
 7             return res
 8         return wrapper
 9     return deco
10 @deco("book")  #裝飾器加引數    跟go=deco("book")(go)類似
11 def go(x,y):
12     print("python",x,y)
13 go(3,5)

3層巢狀的裝飾器的效果是這樣的

1 go=auth_book("book")(go)
 1 user_list=[
 2     {'name':'yj','passwd':'123'},
 3     {'name':'xixi','passwd':'123'},
 4     {'name':'xiha','passwd':'123'},
 5     {'name':'lala','passwd':'123'},
 6 ]
 7 
 8 current_user={'username':None,'login':False}
 9 def auth(auth_type='file'):
10     def auth_deco(func):
11         def wrapper(*args,**kwargs):
12             if auth_type == 'file':
13                 if current_user['username'] and current_user['login']:
14                     res=func(*args,**kwargs)
15                     return res
16                 username=input('使用者名稱: ').strip()
17                 passwd=input('密碼: ').strip()
18 
19                 for index,user_dic in enumerate(user_list):
20                     if username == user_dic['name'] and passwd == user_dic['passwd']:
21                         current_user['username']=username
22                         current_user['login']=True
23                         res=func(*args,**kwargs)
24                         return res
25 
26                 else:
27                     print('使用者名稱或者密碼錯誤,重新登入')
28             elif auth_type == 'ldap':
29                 print('巴拉巴拉小魔仙')
30                 res=func(*args,**kwargs)
31                 return res
32         return wrapper
33     return auth_deco
34 
35 
36 #auth(auth_type='file')就是在執行一個函式,然後返回auth_deco,所以@auth(auth_type='file')
37 #就相當於@auth_deco,只不過現在,我們的auth_deco作為一個閉包的應用,外層的包auth給它留了一個auth_type='file'引數
38 @auth(auth_type='ldap')
39 def index():
40     print('歡迎來到主頁面')
41 
42 @auth(auth_type='ldap')
43 def home():
44     print('這裡是你家')
45 @auth(auth_type="file")
46 def shopping_car():
47     print('檢視購物車啊親')
48 
49 def order():
50     print('檢視訂單啊親')
51 
52 # print(user_list)
53 index()
54 # print(user_list)
55 home()
56 shopping_car()
demo 帶引數的裝飾器

 

 

 

 

1.1迭代器

什麼是迭代器:

迭代器是一個可以記住遍歷的位置物件

迭代器物件從集合的第一個元素元素開始訪問,直到所有元素被訪問完結束,迭代器只能往前不會後退。

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

內建函式iter(),next()  本質上都是用的物件.__iter__(),__next__()的方法

內建函式 iter(iterable),表示把可迭代物件 變成迭代器(iterator)

內建函式next(iterator) ,表示檢視下一次迭代的值(當然也可以用 iterator.__next__() ,檢視下一次迭代的值)

1.1.2迭代器協議

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

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

3.協議是一種約定,可迭代物件實現了迭代器協議,python的內部工具(如。for迴圈,sum,min,max函式等)使用迭代器協議訪問物件

 

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語句返回一個值。掛起該生成器函式的狀態。

 

1.3裝飾器(decorator)

什麼是裝飾器:

器即函式

1.裝飾器可以理解為給一個函式,做修飾,而不修改函式本身。

2.裝飾器定義:本質就是函式,decorator功能就是為其他函式新增新的功能。

裝飾器的的原則