摘要:在學(xué)習(xí)的時(shí)候,三大名器對(duì)沒有其他語(yǔ)言編程經(jīng)驗(yàn)的人來(lái)說(shuō),應(yīng)該算是一個(gè)小難點(diǎn),本次博客就博主自己對(duì)裝飾器迭代器和生成器理解進(jìn)行解釋。
在學(xué)習(xí)python的時(shí)候,三大“名器”對(duì)沒有其他語(yǔ)言編程經(jīng)驗(yàn)的人來(lái)說(shuō),應(yīng)該算是一個(gè)小難點(diǎn),本次博客就博主自己對(duì)裝飾器、迭代器和生成器理解進(jìn)行解釋。
裝飾器什么是裝飾器?“裝飾”從字面意思來(lái)誰(shuí)就是對(duì)特定的建筑物內(nèi)按照一定的思路和風(fēng)格進(jìn)行美化的一種行為,所謂“器”就是工具,對(duì)于python來(lái)說(shuō)裝飾器就是能夠在不修改原始的代碼情況下給其添加新的功能,比如一款軟件上線之后,我們需要在不修改源代碼和不修改被調(diào)用的方式的情況下還能為期添加新的功能,在python種就可以用裝飾器來(lái)實(shí)現(xiàn),同樣在寫代碼的時(shí)候也要考慮到后面的可擴(kuò)展性,下面我們來(lái)看一步一步的看一下python的裝飾器。
無(wú)參裝飾器先來(lái)看簡(jiǎn)單的幾行代碼,代碼的運(yùn)行結(jié)果是先睡2秒,再打印"hello boy!":
import time def foo(): """打印""" time.sleep(2) print("Hello boy!") foo()
我們現(xiàn)在我們需要為其添加一個(gè)程序計(jì)時(shí)功能,但是不能修改原始的代碼:
import time def timmer(func): def wrapper(): """計(jì)時(shí)功能""" time_start=time.time() func() time_end=time.time() print("Run time is %f "%(time_end-time_start)) return wrapper def foo(): """打印""" time.sleep(2) print("Hello boy!") foo = timmer(foo) foo()
運(yùn)行結(jié)果
Hello boy!
Run time is 2.000446
看!我們沒有修改原來(lái)的代碼就實(shí)現(xiàn)了這個(gè)功能,因?yàn)楹瘮?shù)也是對(duì)象,所以能夠?qū)⒑瘮?shù)foo當(dāng)做參數(shù)傳遞給了函數(shù)timmer。
在python中,有個(gè)更簡(jiǎn)潔的方式來(lái)取代foo=timmer(foo),使用@timmer這種方式,這個(gè)在python中被稱為語(yǔ)法糖。
import time def timmer(func): def wrapper(): """計(jì)時(shí)功能""" time_start=time.time() func() time_end=time.time() print("Run time is %f "%(time_end-time_start)) return wrapper @timmer #等于 foo=timmer(foo) def foo(): """打印""" time.sleep(2) print("Hello boy!") foo()
下面我們來(lái)一步一步的分析函數(shù)的執(zhí)行過(guò)程:
1.導(dǎo)入time模塊
import time
2.定義函數(shù)timmer,定義函數(shù)并不會(huì)執(zhí)行函數(shù)內(nèi)的代碼
def timmer(func):
3.調(diào)用裝飾器,相當(dāng)于foo=timer(foo),就是把函數(shù)foo作為參數(shù)穿給了函數(shù)timmer
@timmer
4.運(yùn)行函數(shù)timmer,接受了參數(shù) func=foo
def timmer(func):
5.在函數(shù)timmer內(nèi),定義了函數(shù)wrapper,wrapper函數(shù)內(nèi)部代碼也不執(zhí)行,然后將函數(shù)wrapper作為返回值返回
return wrapper
6.將返回值賦值給了foo,在第3步中,foo=timmer(foo),還記吧
@timmer #等于 foo=timmer(foo)
7.運(yùn)行函數(shù)foo(),但是這里的函數(shù)已經(jīng)不是原來(lái)的那個(gè)函數(shù)了,可以打印foo,對(duì)的,因?yàn)橹拔覀儗rapper作為返回值傳給了foo,所以在這里執(zhí)行foo就是在執(zhí)行wrapper了,為了再確定這一點(diǎn)你也可打印wrapper,它們的內(nèi)存地址相同,所以都是指向同一個(gè)地址空間:
.wrapper at 0x00000180E0A8A950> #打印foo的結(jié)果 .wrapper at 0x000001F10AD8A950> #打印wrapper的結(jié)果 foo()
8.運(yùn)行函數(shù)wrapper,記錄開始時(shí)間,執(zhí)行函數(shù)func,在第4步的時(shí)候,func被foo賦值,運(yùn)行func就是在運(yùn)行原函數(shù)foo,睡2秒,打印字符串;
time_start=time.time() time.sleep(2) print("Hello boy!")
9.記錄結(jié)束時(shí)間,打印運(yùn)行時(shí)間,程序結(jié)束。
Hello boy!
Run time is 2.000161
在前面的例子中,原函數(shù)沒有參數(shù),下面的來(lái)看一個(gè)當(dāng)原函數(shù)有參數(shù),該怎么修改裝飾器函數(shù)呢?
# -*- coding: UTF-8 -*- import time def timmer(func): def wrapper(*args,**kwargs): """計(jì)時(shí)功能""" start_time=time.time() res=func(*args,**kwargs) end_time=time.time() print("Run time is %f"%(end_time-start_time)) return res return wrapper @timmer def my_max(x,y): """返回兩個(gè)值的最大值""" res=x if x > y else y time.sleep(2) return res res=my_max(1,2) print(res)
運(yùn)行結(jié)果
Run time is 2.000175
2
當(dāng)原函數(shù)有需要傳入?yún)?shù)的時(shí)候,在這個(gè)例子my_max有兩個(gè)位置形成需要傳入?yún)?shù),只需要在wrapper上添加兩個(gè)形參,本例子中使用了可變參數(shù)(args,*kwargs)也是可以的,這是@timmer就等于my_max(1,2)=timmer(my_max)(1,2)
下面我們來(lái)看一個(gè)帶有參數(shù)的裝飾器:
def auth(filetype): def auth2(func): def wrapper(*args,**kwargs): if filetype == "file": username=input("Please input your username:") passwd=input("Please input your password:") if passwd == "123456" and username == "Frank": print("Login successful") func() else: print("login error!") if filetype == "SQL": print("No SQL") return wrapper return auth2 @auth("file) def index(): print("Welcome to China") index()
如果裝飾器本身有參數(shù),就需要多一層內(nèi)嵌函數(shù),下面我們一步一步分析執(zhí)行流程:
1.定義函數(shù)auth
def auth(filetype):
2.調(diào)用解釋器,首先要運(yùn)行函數(shù)auth(filetype="file")
@auth(filetype="file")
3.運(yùn)行函數(shù)auth,定義了一個(gè)函數(shù)auth2,并作為返回值返回,那么這個(gè)@auth(filetype="file")就等同于@auth2,等同于index=auth2(index)
def auth(filetype): def auth2(func): def wrapper(*args,**kwargs): return wrapper return auth2
4.auth2(index)執(zhí)行,func=index,定義函數(shù)wrapper,并返回之,這時(shí)候index其實(shí)就是等于wrapper了
def wrapper(*args,**kwargs): return wrapper
5.當(dāng)運(yùn)行index,即運(yùn)行wrapper,運(yùn)行函數(shù)內(nèi)部代碼,filetype=="file",提示用戶輸出用戶名和密碼,判斷輸入是否正確,如果正確,則執(zhí)行函數(shù)func(),等于執(zhí)行原來(lái)的index,打印
if filetype == "file": username=input("Please input your username:") passwd=input("Please input your password:") if passwd == "123456" and username == "Frank": print("Login successful") func()
6.運(yùn)行結(jié)果測(cè)試
Please input your username:Frank
Please input your password:123456
Login successful
Welcome to China
裝飾器也是可以被疊加的:
import time def timmer(func): def wrapper(): """計(jì)時(shí)功能""" time_start = time.time() func() time_end = time.time() print("Run time is %f " % (time_end - time_start)) # print("---",wrapper) return wrapper def auth(filetype): def auth2(func): def wrapper(*args, **kwargs): if filetype == "file": username = input("Please input your username:") passwd = input("Please input your password:") if passwd == "123456" and username == "Frank": print("Login successful") func() else: print("login error!") if filetype == "SQL": print("No SQL") return wrapper return auth2 @timmer # 先先返回一個(gè)auth2 ==》@auth2 ==》 index=auth2() ==》 index=wrapper @auth(filetype="file") def index(): print("Welcome to China") index()
測(cè)試結(jié)果
Please input your username:Frank
Please input your password:123456
Login successful
Welcome to China
Run time is 7.966267
注釋優(yōu)化
import time def timmer(func): def wrapper(): """計(jì)算程序運(yùn)行時(shí)間""" start_time = time.time() func() end_time = time.time() print("Run time is %s:" % (end_time - start_time)) return wrapper @timmer def my_index(): """打印歡迎""" time.sleep(1) print("Welcome to China!") my_index()
運(yùn)行結(jié)果
Welcome to China!
Run time is 1.0005640983581543:
計(jì)算程序運(yùn)行時(shí)間
當(dāng)我們使用了裝飾器的時(shí)候,雖然沒有修改代碼本身,但是在運(yùn)行的時(shí)候,比如上面這個(gè)例子,運(yùn)行my_index其實(shí)在運(yùn)行wrapper了,如果我們打印my_index的注釋信息,會(huì)打印wrapper()的注釋信息,那么該怎么優(yōu)化?
import time from functools import wraps def timmer(func): @wraps(func) def wrapper(): """計(jì)算程序運(yùn)行時(shí)間""" start_time = time.time() func() end_time = time.time() print("Run time is %s:" % (end_time - start_time)) return wrapper @timmer def my_index(): """打印歡迎""" time.sleep(1) print("Welcome to China!") my_index() print(my_index.__doc__)
相當(dāng)于執(zhí)行
無(wú)參數(shù) timmer(now)(1) # 有參數(shù) timmer("n")(now)(1)
運(yùn)行結(jié)果
Welcome to China!
Run time is 1.0003223419189453:
打印歡迎
這樣,在表面看來(lái),原函數(shù)沒有發(fā)生任何變化。
從字面意思,迭代就是重復(fù)反饋過(guò)程的活動(dòng),其目的通常是為了比較所需目標(biāo)或結(jié)果,在python中可以用迭代器來(lái)實(shí)現(xiàn),先來(lái)描述一下迭代器的優(yōu)缺點(diǎn),如果看不懂可以先略過(guò),等看完本博客再回頭看,相信你會(huì)理解其中的意思:
優(yōu)點(diǎn):
迭代器在取值的時(shí)候是不依賴于索引的,這樣就可以遍歷那些沒有索引的對(duì)象,比如字典和文件
迭代器與列表相比,迭代器是惰性計(jì)算,更節(jié)省內(nèi)存
缺點(diǎn):
無(wú)法獲取迭代器的長(zhǎng)度,沒有列表靈活
只能往后取值,不能倒著取值
什么是迭代器
那么在python什么才算是迭代器呢?
只要對(duì)象有__iter__(),那么它就是可迭代的,迭代器可以使用函數(shù)next()來(lái)取值
下面我們來(lái)看一個(gè)簡(jiǎn)單的迭代器:
my_list=[1,2,3] li=iter(my_list) #li=my_list.__iter__() print(li) print(next(li)) print(next(li)) print(next(li))
運(yùn)行結(jié)果
2
可以看到,使用內(nèi)置函數(shù)iter可以將列表轉(zhuǎn)換成一個(gè)列表迭代器,使用next()獲取值,一次值取一個(gè)值,當(dāng)值取完了,再使用一次next()的時(shí)候,會(huì)報(bào)異常StopIteration,可以通過(guò)異常處理的方式來(lái)避免,try-except-else就是一個(gè)最常用的異常處理結(jié)構(gòu):
my_list=[1,2,3] li=iter(my_list) while True: try: print(next(li)) except StopIteration: print("Over") break else: print("get!")
運(yùn)行結(jié)果
get!
get!
get!
Over
查看可迭代對(duì)象和迭代器對(duì)象
使用Iterable模塊可以判斷對(duì)象是否是可迭代的:
from collections import Iterable s="hello" #定義字符串 l=[1,2,3,4] #定義列表 t=(1,2,3) #定義元組 d={"a":1} #定義字典 set1={1,2,3,4} #定義集合 f=open("a.txt") #定義文本 #查看是否都是可迭代的 print(isinstance(s,Iterable)) print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(set1,Iterable)) print(isinstance(f,Iterable))
運(yùn)行結(jié)果
True
True
True
True
True
True
通過(guò)判斷,可以確定我們所知道的常用的數(shù)據(jù)類型都是可以被迭代的。
使用Iterator模塊可以判斷對(duì)象是否是迭代器:
from collections import Iterable,Iterator s="hello" l=[1,2,3,4] t=(1,2,3) d={"a":1} set1={1,2,3,4} f=open("a.txt") # 查看是否都是可迭代的 print(isinstance(s,Iterator)) print(isinstance(l,Iterator)) print(isinstance(t,Iterator)) print(isinstance(d,Iterator)) print(isinstance(set1,Iterator)) print(isinstance(f,Iterator))
運(yùn)行結(jié)果
False
False
False
False
False
True
可知只有文件是迭代器,所以可以直接使用next(),而不需要轉(zhuǎn)換成迭代器。
生產(chǎn)器就是一個(gè)是帶有yield的函數(shù)
下面來(lái)看一個(gè)簡(jiǎn)單的生成器
def my_yield(): print("first") yield 1 g = my_yield() print(g)
運(yùn)行結(jié)果
生成器也是一個(gè)迭代器
from collections import Iterator def my_yield(): print("first") yield 1 g = my_yield() print(isinstance(g, Iterator))
運(yùn)行結(jié)果
True
那就可以用next()來(lái)取值了
print(next(g))
運(yùn)行結(jié)果
first
1
生成器的執(zhí)行過(guò)程
我們來(lái)看以下下面這個(gè)例子,了解生產(chǎn)的執(zhí)行流程
def my_yield(): print("first") yield 1 print("second") yield 2 print("Third") yield 3 g=my_yield() next(g) next(g) next(g)
運(yùn)行結(jié)果
first
second
Third
1.定義生成器my_yield,并將其賦值給了g
def my_yield(): g=my_yield()
2.開始第一次執(zhí)行next(),開始執(zhí)行生產(chǎn)器函數(shù) ,打印第一語(yǔ)句,遇到y(tǒng)ileld的時(shí)候暫停,并返回一個(gè)1,如果你想打印返回值的話,這里會(huì)顯示1
print("first") yield 1
3.再執(zhí)行2次,打印字符串(每執(zhí)行一次都會(huì)暫停一下)
print("second") yield 2 print("Third") yield 3
**4.如果再加一次next()就會(huì)報(bào)出StopIteration異常了
生成器在每次暫停的時(shí)候,函數(shù)的狀態(tài)將被保存下來(lái),來(lái)看下面的例子:**
def foo(): i=0 while True: yield i i+=1 g=foo() for num in g: if num < 10: print(num) else: break
運(yùn)行結(jié)果
for循環(huán)中隱含next(),每next一次,暫停一次,if語(yǔ)句判斷一次,然后執(zhí)行下一次next,可以看到我們的while循環(huán)并沒有無(wú)限循環(huán)下去,而是狀態(tài)被保存下來(lái)了。
協(xié)程函數(shù)
我們來(lái)看下面這個(gè)生成器和執(zhí)行結(jié)果
def eater(name): print("%s start to eat food"%name) while True: food=yield print("%s get %s ,to start eat"%(name,food)) print("done") e=eater("Frank") next(e)#或者e.send(None) e.send("egg") #給yield送一個(gè)值,并繼續(xù)執(zhí)行代碼 e.send("tomato")
運(yùn)行結(jié)果
Frank start to eat food
Frank get egg ,to start eat
Frank get tomato ,to start eat
send可直接以向yield傳值,含有yield表達(dá)式的函數(shù)我們也稱為協(xié)程函數(shù),
這運(yùn)行程序的時(shí)候,不可以直接send,必須先使用next()初始化生成器(e.send(None)亦可以初始化)。
如果存在多個(gè)這樣的函數(shù),那么我們每次執(zhí)行的時(shí)候都要去next()一下,為了防止忘記這一步操作,可以使用裝飾器初始化:
def init(func): def wrapper(*args): res = func(*args) next(res) # 在這里執(zhí)行next return res return wrapper @init def eater(name): print("%s start to eat food"%name) while True: food=yield print("%s get %s ,to start eat"%(name,food)) print("done") e=eater("Frank") e.send("egg") e.send("tomato")
所以在程序中有更多的生成器需要初始化的時(shí)候,直接調(diào)用這個(gè)裝飾器就可以了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/41026.html
摘要:上下文管理器協(xié)議包含和兩個(gè)方法。因此必要時(shí)在上下文管理器函數(shù)中使用語(yǔ)句防范錯(cuò)誤。構(gòu)建臨時(shí)忽略指定異常的上下文管理器。這是個(gè)基類,用于定義基于類的上下文管理器。塊結(jié)束時(shí),按照后進(jìn)先出的順序調(diào)用棧中各個(gè)上下文管理器的方法。 導(dǎo)語(yǔ):本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之控制流程篇的重點(diǎn)知識(shí)及個(gè)人心得,打算入門Python的朋友們可以來(lái)一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、掌握if語(yǔ)句之外的el...
摘要:前言首先,明確可迭代對(duì)象迭代器和生成器這三個(gè)概念。迭代器對(duì)象傳送門之迭代器實(shí)現(xiàn)原理首先明確它是一個(gè)帶狀態(tài)的對(duì)象。生成器是一種特殊的迭代器,它的返回值不是通過(guò)而是用。 前言 首先,明確可迭代對(duì)象、迭代器和生成器這三個(gè)概念。 可迭代對(duì)象(Iterable) 可迭代對(duì)象(Iterable Object),簡(jiǎn)單的來(lái)理解就是可以使用 for 來(lái)循環(huán)遍歷的對(duì)象。比如常見的 list、set和di...
摘要:迭代器迭代是訪問(wèn)集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對(duì)象,迭代器對(duì)象從集合的第一個(gè)元素開始訪問(wèn),直到所有的元素被訪問(wèn)完結(jié)束,迭代器只往前不會(huì)往后退。生成器特點(diǎn)保存了一套生成數(shù)值的算法。 迭代器 迭代是訪問(wèn)集合元素的一種方式。迭代器是一個(gè)可以記住遍歷的位置的對(duì)象,迭代器對(duì)象從集合的第一個(gè)元素開始訪問(wèn),直到所有的元素被訪問(wèn)完結(jié)束,迭代器只往前不會(huì)往后退。 可迭代對(duì)象 以直接...
摘要:裝飾器的應(yīng)用場(chǎng)景比如插入日志,性能測(cè)試,事務(wù)處理,緩存等等場(chǎng)景。裝飾器完美的遵循了這個(gè)開放封閉原則。迭代器迭代器遵循迭代器協(xié)議必須擁有方法和方法。直到函數(shù)執(zhí)行結(jié)束。調(diào)用相關(guān)函數(shù)用于檢查一個(gè)對(duì)象是否是可調(diào)用的。 裝飾器 裝飾器的含義: 1.裝飾器本質(zhì)上就是一個(gè)python函數(shù),他可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下,增加額外的功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。2.裝飾器的應(yīng)用...
摘要:導(dǎo)語(yǔ)本文章匯總了本人在學(xué)習(xí)基礎(chǔ)之緒論篇數(shù)據(jù)結(jié)構(gòu)篇函數(shù)篇面向?qū)ο笃刂屏鞒唐驮幊唐獙W(xué)習(xí)筆記的鏈接,打算入門的朋友們可以按需查看并交流。 導(dǎo)語(yǔ):本文章匯總了本人在學(xué)習(xí)Python基礎(chǔ)之緒論篇、數(shù)據(jù)結(jié)構(gòu)篇、函數(shù)篇、面向?qū)ο笃⒖刂屏鞒唐驮幊唐獙W(xué)習(xí)筆記的鏈接,打算入門Python的朋友們可以按需查看并交流。 第一部分:緒論篇 1、Python數(shù)據(jù)模型 第二部分:數(shù)據(jù)結(jié)構(gòu)篇 2、序列構(gòu)成...
閱讀 2994·2023-04-25 21:23
閱讀 3030·2021-09-22 15:24
閱讀 864·2019-08-30 12:55
閱讀 2099·2019-08-29 18:42
閱讀 2610·2019-08-29 16:27
閱讀 950·2019-08-26 17:40
閱讀 2183·2019-08-26 13:29
閱讀 2610·2019-08-26 11:45