python裝飾器實(shí)例大詳解
一.作用域
在python中,作用域分為兩種:全局作用域和局部作用域。
全局作用域是定義在文件級(jí)別的變量,函數(shù)名。而局部作用域,則是定義函數(shù)內(nèi)部。
關(guān)于作用域,我們要理解兩點(diǎn):
a.在全局不能訪問(wèn)到局部定義的變量
b.在局部能夠訪問(wèn)到全局定義的變量,但是不能修改全局定義的變量(當(dāng)然有方法可以修改)
下面我們來(lái)看看下面實(shí)例:
x = 1 def funx(): x = 10 print(x) # 打印出10 funx() print(x) # 打印出1
如果局部沒(méi)有定義變量x,那么函數(shù)內(nèi)部會(huì)從內(nèi)往外開(kāi)始查找x,如果沒(méi)有找到,就會(huì)報(bào)錯(cuò)
x = 1 def funx(): print(x) funx() print(x) # 打印出1 x = 1 def funx(): def func1(): print(x) func1() funx() print(x) # 打印出1
因此,關(guān)于作用域的問(wèn)題,只需要記住兩點(diǎn)就行:
全局變量能夠被文件任何地方引用,但修改只能在全局進(jìn)行操作;如果局部沒(méi)有找到所需的變量,就會(huì)往外進(jìn)行查找,沒(méi)有找到就會(huì)報(bào)錯(cuò)。
二.高級(jí)函數(shù)
我們知道,函數(shù)名其實(shí)就是指向一段內(nèi)存空間的地址,既然是地址,那么我們可以利用這種特性來(lái)。
def delete(ps): import os filename = ps[-1] delelemetns = ps[1] with open(filename, encoding='utf-8') as f_read,\ open('tmp.txt', 'w', encoding='utf-8') as f_write: for line in iter(f_read.readline, ''): if line != '\n': # 處理非空行 if delelemetns in line: line = line.replace(delelemetns,'') f_write.write(line) os.remove(filename) os.rename('tmp.txt',filename) def add(ps): filename = ps[-1] addelemetns = ps[1] with open(filename, 'a', encoding='utf-8') as fp: fp.write("\n", addelemetns) def modify(ps): import os filename = ps[-1] modify_elemetns = ps[1] with open(filename, encoding='utf-8') as f_read, \ open('tmp.txt', 'w', encoding='utf-8') as f_write: for line in iter(f_read.readline, ''): if line != '\n': # 處理非空行 if modify_elemetns in line: line = line.replace(modify_elemetns, '') f_write.write(line) os.remove(filename) os.rename('tmp.txt', filename) def search(cmd): filename = cmd[-1] pattern = cmd[1] with open(filename, 'r', encoding="utf-8") as f: for line in f: if pattern in line: print(line, end="") else: print("沒(méi)有找到") dic_func ={'delete': delete, 'add': add, 'modify': modify, 'search': search} while True: inp = input("請(qǐng)輸入您要進(jìn)行的操作:").strip() if not inp: continue cmd_1 = inp.split() cmd = cmd_1[0] if cmd in dic_func: dic_func[cmd](cmd_1) else: print("Error")
將函數(shù)作為字典值,實(shí)現(xiàn)文本數(shù)據(jù)的增刪查改操作
b.函數(shù)名可以作為返回值
def outer(): def inner(): pass return inner s = outer() print(s) ######輸出結(jié)果為####### <function outer.<locals>.inner at 0x000000D22D8AB8C8>
c.函數(shù)名可以作為一個(gè)參數(shù)
def index(): print("index func") def outer(index): s = index s() outer(index) ######輸出結(jié)果######### index func
所以滿足上面兩個(gè)條件中的一個(gè),都可以稱為高級(jí)函數(shù).
三.閉包函數(shù)
閉包函數(shù)必須滿足兩個(gè)條件:1.函數(shù)內(nèi)部定義的函數(shù) 2.包含對(duì)外部作用域而非全局作用域的引用
下面通過(guò)一些實(shí)例來(lái)說(shuō)明閉包函數(shù):
實(shí)例一:以下僅僅在函數(shù)內(nèi)部定義了一個(gè)函數(shù),但并非閉包函數(shù).
def outer(): def inner(): print("inner func excuted") inner() # 調(diào)用執(zhí)行inner()函數(shù) print("outer func excuted") outer() # 調(diào)用執(zhí)行outer函數(shù) ####輸出結(jié)果為########## inner func excuted outer func excuted
實(shí)例二:以下在函數(shù)內(nèi)部定義了一個(gè)函數(shù),而且還引用了一個(gè)外部變量x,那么這個(gè)是閉包函數(shù)么?答案:不是
x = 1 def outer(): def inner(): print("x=%s" %x) # 引用了一個(gè)非inner函數(shù)內(nèi)部的變量 print("inner func excuted") inner() # 執(zhí)行inner函數(shù) print("outer func excuted") outer() #####輸出結(jié)果######## x=1 inner func excuted outer func excuted
在回頭來(lái)看看對(duì)閉包函數(shù)的定義,是不是兩條都滿足?聰明的你,一定發(fā)現(xiàn)不滿足第二條.對(duì),這里的變量x,是屬于全局變量,而非外部作用于域的變量。再來(lái)看看下面例子:
def outer(): x = 1 def inner(): print("x=%s" %x) print("inner func excuted") inner() print("outer func excuted") outer() #####輸出結(jié)果######### x=1 inner func excuted outer func excuted
顯然,上面實(shí)例滿足閉包函數(shù)的條件?,F(xiàn)在,你應(yīng)該清楚,作為一個(gè)閉包函數(shù),必須得滿足上述的兩個(gè)條件,缺一不可。但是,一般情況下,我們都會(huì)給閉包函數(shù)返回一個(gè)值.這里先不說(shuō)為什么.在接下來(lái)的內(nèi)容中,你會(huì)看到這個(gè)返回值的用途.
def outer(): x = 1 def inner(): print("x=%s" %x) print("inner func excuted") print("outer func excuted") return inner # 返回內(nèi)部函數(shù)名 outer()
現(xiàn)在我們來(lái)抽象的定義一下閉包函數(shù)。它是函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。在實(shí)現(xiàn)深約束時(shí),需要?jiǎng)?chuàng)建一個(gè)能顯式表示引用環(huán)境的東西,并將它與相關(guān)的子程序捆綁在一起,這樣捆綁起成為閉包。在上面實(shí)例中,我們可以發(fā)現(xiàn),閉包函數(shù),它必須包含自己的函數(shù)以及一個(gè)外部變量才能真正稱得上是一個(gè)閉包函數(shù)。如果沒(méi)有一個(gè)外部變量與其綁定,那么這個(gè)函數(shù)不能算得上是閉包函數(shù)。
那么怎么知道一個(gè)閉包函數(shù)有多少個(gè)外部引用變量呢?看看下面代碼.
def outer(): x = 1 y = 2 def inner(): print("x= %s" %x) print("y= %s" %y) print(inner.__closure__) return inner outer() ######輸出結(jié)果####### (<cell at 0x000000DF9EA965B8: int object at 0x000000006FC2B440>, <cell at 0x000000DF9EA965E8: int object at 0x000000006FC2B460>)
結(jié)果表明,在inner內(nèi)部,引用了兩個(gè)外部局部變量。如果引用的是非局部變量,那么這里輸出的為None.
閉包函數(shù)的特點(diǎn):1.自帶作用域 2.延遲計(jì)算
那么閉包函數(shù)有什么作用呢?我們清楚的知道,閉包函數(shù)在定義時(shí),一定會(huì)綁定一個(gè)外部環(huán)境。這個(gè)整體才能算的上是一個(gè)閉包函數(shù),那么我們可以利用這個(gè)綁定特性,來(lái)完成某些特殊的功能。
實(shí)例三:根據(jù)傳入的URL,來(lái)下載頁(yè)面源碼
from urllib.request import urlopen def index(url) def get() return urlopen(url).read() return get python = index("http://www.python.org") # 返回的是get函數(shù)的地址 print(python()) # 執(zhí)行g(shù)et函數(shù)《并且將返回的結(jié)果打印出來(lái) baidu = index("http://www.baidu.com") print(baidu())
有人可以會(huì)說(shuō),這個(gè)不滿足閉包函數(shù)的條件啊!我沒(méi)有引用非全局的外部變量啊。其實(shí)并非如此,給,我們之前說(shuō)過(guò),只要在函數(shù)內(nèi)部的變量都屬于函數(shù)。那么我在index(url),這個(gè)url也屬于函數(shù)內(nèi)部,只不過(guò)我們省略一步而已,所以上面那個(gè)函數(shù)也是閉包函數(shù)。
四.裝飾器
有了以上基礎(chǔ),對(duì)于裝飾器就好理解了.
裝飾器:外部函數(shù)傳入被裝飾函數(shù)名,內(nèi)部函數(shù)返回裝飾函數(shù)名。
特點(diǎn):1.不修改被裝飾函數(shù)的調(diào)用方式 2.不修改被裝飾函數(shù)的源代碼
a.無(wú)參裝飾器
有如下實(shí)例,我們需要計(jì)算一下代碼執(zhí)行的時(shí)間。
import time, random def index(): time.sleep(random.randrange(1, 5)) print("welcome to index page")
根據(jù)裝飾器的特點(diǎn),我們不能對(duì)index()進(jìn)行任何修改,而且調(diào)用方式也不能變。這時(shí)候,我們就可以使用裝飾器來(lái)完成如上功能.
import time, random def outer(func): # 將index的地址傳遞給func def inner(): start_time = time.time() func() # fun = index 即func保存了外部index函數(shù)的地址 end_time = time.time() print("運(yùn)行時(shí)間為%s"%(end_time - start_time)) return inner # 返回inner的地址 def index(): time.sleep(random.randrange(1, 5)) print("welcome to index page") index = outer(index) # 這里返回的是inner的地址,并重新賦值給index index()
但是,有些情況,被裝飾的函數(shù)需要傳遞參數(shù)進(jìn)去,有些函數(shù)又不需要參數(shù),那么如何來(lái)處理這種變參數(shù)函數(shù)呢?下面來(lái)看看有參數(shù)裝飾器的使用情況.
b.有參裝飾器
def outer(func): # 將index的地址傳遞給func def inner(*args, **kwargs): start_time = time.time() func(*args, **kwargs) # fun = index 即func保存了外部index函數(shù)的地址 end_time = time.time() print("運(yùn)行時(shí)間為%s"%(end_time - start_time)) return inner # 返回inner的地址
下面來(lái)說(shuō)說(shuō)一些其他情況的實(shí)例。
如果被裝飾的函數(shù)有返回值
def timmer(func): def wrapper(*args,**kwargs): start_time = time.time() res=func(*args,**kwargs) #res來(lái)接收home函數(shù)的返回值 stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapper def home(name): time.sleep(random.randrange(1,3)) print('welecome to %s HOME page' %name) return 123123123123123123123123123123123123123123
這里補(bǔ)充一點(diǎn),加入我們要執(zhí)行被裝飾后的函數(shù),那么應(yīng)該是如下調(diào)用方式:
home = timmer(home) # 等式右邊返回的是wrapper的內(nèi)存地址,再將其賦值給home,這里的home不在是原來(lái)的的那個(gè)函數(shù),而是被裝飾以后的函數(shù)了。
像home = timmer(home)這樣的寫(xiě)法,python給我們提供了一個(gè)便捷的方式------語(yǔ)法糖@.
以后我們?cè)僖诒谎b飾的函數(shù)之前寫(xiě)上@timmer,它的效果就和home = timmer(home)是一樣的。
如果一個(gè)函數(shù)被多個(gè)裝飾器裝飾,那么執(zhí)行順序是怎樣的。
import time import random def timmer(func): def wrapper(): start_time = time.time() func() stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return wrapper def auth(func): def deco(): name=input('name: ') password=input('password: ') if name == 'egon' and password == '123': print('login successful') func() #wrapper() else: print('login err') return deco @auth # index = auth(timmer(index)) @timmer # index = timmer(index) def index(): time.sleep(3) print('welecome to index page') index()
實(shí)驗(yàn)結(jié)果表明,多個(gè)裝飾器裝飾一個(gè)函數(shù),其執(zhí)行順序是從下往上。
總結(jié)
以上所述是小編給大家介紹的python裝飾器實(shí)例大詳解,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
在keras中獲取某一層上的feature map實(shí)例
今天小編就為大家分享一篇在keras中獲取某一層上的feature map實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01python用pd.read_csv()方法來(lái)讀取csv文件的實(shí)現(xiàn)
本文主要介紹了python用pd.read_csv()方法來(lái)讀取csv文件的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06Python+Selenium實(shí)現(xiàn)短視頻自動(dòng)上傳與發(fā)布的實(shí)踐
本文主要介紹了Python+Selenium實(shí)現(xiàn)短視頻自動(dòng)上傳與發(fā)布的實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04python腳本實(shí)現(xiàn)音頻m4a格式轉(zhuǎn)成MP3格式的實(shí)例代碼
這篇文章主要介紹了python腳本實(shí)現(xiàn)音頻m4a格式轉(zhuǎn)成MP3格式的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10聊聊Python pandas 中l(wèi)oc函數(shù)的使用,及跟iloc的區(qū)別說(shuō)明
這篇文章主要介紹了聊聊Python pandas 中l(wèi)oc函數(shù)的使用,及跟iloc的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03python實(shí)戰(zhàn)之實(shí)現(xiàn)excel讀取、統(tǒng)計(jì)、寫(xiě)入的示例講解
下面小編就為大家分享一篇python實(shí)戰(zhàn)之實(shí)現(xiàn)excel讀取、統(tǒng)計(jì)、寫(xiě)入的示例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Python實(shí)現(xiàn)感知器模型、兩層神經(jīng)網(wǎng)絡(luò)
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)感知器模型、兩層神經(jīng)網(wǎng)絡(luò),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12