通俗易懂了解Python裝飾器原理
作用
裝飾器可以用于用于裝飾一個(gè)函數(shù)或方法,使得在不修改原函數(shù)、方法代碼的前提下,為方法添加前置或后置操作;
例如突然想要計(jì)算一下各個(gè)函數(shù)的執(zhí)行時(shí)間,又不希望在每一個(gè)函數(shù)中添加tim.time()來計(jì)算執(zhí)行時(shí)間
用法
裝飾器的寫法網(wǎng)上很多,但是我覺得還是盡量先理解,再知道怎么寫會比較好,所以會先說如何理解,在后面重寫用法
實(shí)現(xiàn)
了解裝飾器是如何實(shí)現(xiàn)的,遠(yuǎn)比會寫裝飾器更重要,簡單的說裝飾器就是接收一個(gè)函數(shù)對象,然后先執(zhí)行前置操作,再執(zhí)行函數(shù),再執(zhí)行后置操作;
這么說可能有些抽象,或者舉一個(gè)不那么恰當(dāng)?shù)谋容^貼近生活的例子;
假設(shè)你有一臺像這樣的小風(fēng)扇:
這臺風(fēng)扇可以充電,有一個(gè)開關(guān),打開之后扇葉會旋轉(zhuǎn),開始工作,當(dāng)然你也可以插著電打開開關(guān),也可以充好電之后帶走,在其他地方打開開關(guān)
如果把這個(gè)風(fēng)扇置于一切皆是對象的Python中,風(fēng)扇就是一個(gè)對象,他實(shí)現(xiàn)的功能就是出風(fēng):
def fengshan(): return '出風(fēng)'
為了更好和例子結(jié)合,我們用pinyin命名
現(xiàn)在我們實(shí)現(xiàn)了一個(gè)fengshan函數(shù),返回吹風(fēng)
如果你稍微有點(diǎn)基礎(chǔ),你就能知道如何調(diào)用這個(gè)方法
def fengshan(): return '出風(fēng)' print(fengshan())
不要覺得這很基礎(chǔ)很墨跡,如果需要理解裝飾器,你必須知道,調(diào)用函數(shù)的方式是函數(shù)名稱加上括號fengshan()
而這個(gè)基礎(chǔ)中的基礎(chǔ)中的括號()就是執(zhí)行函數(shù)的開關(guān),如果我們不加括號
def fengshan(): return '出風(fēng)' print(fengshan)
返回的將是一個(gè)函數(shù)對象(例子中的風(fēng)扇本身)
<function fengshan at 0x7f8e7c4a6950>
這里的意思是 一個(gè)叫fengshan的funciont,地址在0x7f8e7c4a6950
那現(xiàn)在我們就可以把風(fēng)扇帶走,在其他地方使用
def fengshan(): return '出風(fēng)' func = fengshan print(fengshan) print(func)
返回
<function fengshan at 0x7f570eaf3950>
<function fengshan at 0x7f570eaf3950>
這說明func和fengshan是等價(jià)的,他們在同一塊內(nèi)存中,所以當(dāng)我們執(zhí)行func() 也等價(jià)于執(zhí)行fengshan
def fengshan():
return '出風(fēng)'
func = fengshan
print('下面是執(zhí)行fengshan')
print(fengshan())
print('下面是執(zhí)行func')
print(func())
返回
下面是執(zhí)行fengshan
出風(fēng)
下面是執(zhí)行func
出風(fēng)
理解到這里之后你也就能理解裝飾器的實(shí)現(xiàn)了,讓我們再看一個(gè)例子
def fengshan(): return '出風(fēng)' def wrapper(func): return func print(fengshan) print(wrapper(fengshan))
這個(gè)例子中我們除了保留剛剛一直在用的fengshan函數(shù)之外,又定義了一個(gè)wrapper
因?yàn)閜ython中一切皆是對象,函數(shù)也是對象,而函數(shù)的入?yún)⒁部梢越邮諏ο?,所以函?shù)對象可以作為參數(shù)傳遞給另一個(gè)函數(shù)wrapper
這個(gè)wrapper中什么都沒有做,只是返回了接收的func對象,我們打印出來兩個(gè)對象,可以發(fā)現(xiàn)他們其實(shí)是同一個(gè)對象
<function fengshan at 0x7f9b0c92f950>
<function fengshan at 0x7f9b0c92f950>
現(xiàn)在你就已經(jīng)理解了裝飾器的實(shí)現(xiàn)了,而且如果你跟著文中的代碼敲一遍,你就已經(jīng)寫了一個(gè)裝飾器,你只需要稍加修改,比方說,我們在wrapper內(nèi)部執(zhí)行接收的func,并且,在前后加上一些操作
def wrapper(func):
print('在wrapper中執(zhí)行func前')
print(func())
print('在wrapper中執(zhí)行func后')
wrapper(fengshan)
返回
在wrapper中執(zhí)行func前
出風(fēng)
在wrapper中執(zhí)行func后
如果你覺得很神奇,無法理解,可以回到風(fēng)扇的圖片重新再讀一遍,當(dāng)你理解了上述的代碼之后,我們就可以加快速度,完成真正的裝飾器的編寫
用法
當(dāng)你成功明白了函數(shù)被定義和調(diào)用的過程之后,我們開始考慮實(shí)際場景,上一段代碼中我們還是改變了原有函數(shù)的調(diào)用
從調(diào)用fengshan變成了調(diào)用func(fengshan)
我們想要實(shí)現(xiàn)不改變原有代碼和調(diào)用方式的情況下為原有函數(shù)添加前置或后置操作,就需要再優(yōu)化一下我們的裝飾器
def fengshan():
print('出風(fēng)')
def wrapper(func):
def f():
print('在wrapper中執(zhí)行func前')
func()
print('在wrapper中執(zhí)行func后')
return f
fengshan = wrapper(fengshan)
fengshan()
為了看到執(zhí)行過程,我們把fengshan內(nèi)部的return改為print
返回
在wrapper中執(zhí)行func前
出風(fēng)
在wrapper中執(zhí)行func后
這里我們的改變是在wrapper原有的實(shí)現(xiàn)中又包了一層方法f,再回想一下我們前面風(fēng)扇的例子,現(xiàn)在當(dāng)我們執(zhí)行wrapper的時(shí)候,執(zhí)行了什么?
wrapper(func)的返回,是一個(gè)函數(shù)對象f,這個(gè)函數(shù)對象的開關(guān)沒有被打開,f中的代碼不會被執(zhí)行
我們又用同名的fengshan對象去接收了這個(gè)函數(shù)對象f,在最后一行打開fengshan的開關(guān) -- fengshan(),這時(shí)候函數(shù)對象f中的代碼,才剛被執(zhí)行
如果看不懂的話,建議從風(fēng)扇圖片開始再看一遍,如果你看懂了,建議你也再看一遍,至此,我們就已經(jīng)完成了一個(gè)裝飾器,為了更方便使用裝飾器,Python給我們提供了更簡便的方法
def wrapper(func):
def f():
print('在wrapper中執(zhí)行func前')
func()
print('在wrapper中執(zhí)行func后')
return f
@wrapper
def fengshan():
print('出風(fēng)')
fengshan()
返回
在wrapper中執(zhí)行func前
出風(fēng)
在wrapper中執(zhí)行func后
補(bǔ)充知識
如果你已經(jīng)理解了裝飾器的執(zhí)行邏輯,你也就會知道如何讓裝飾器支持帶參數(shù)的方法,這也是我們寫裝飾器的常規(guī)操作
def wrapper(func):
def f(*args, **kwargs):
print('在wrapper中執(zhí)行func前')
func(*args, **kwargs)
print('在wrapper中執(zhí)行func后')
return f
@wrapper
def fengshan(str_obj):
print(str_obj)
fengshan(str_obj='出風(fēng)')
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python實(shí)現(xiàn)決策樹C4.5算法詳解(在ID3基礎(chǔ)上改進(jìn))
下面小編就為大家?guī)硪黄猵ython實(shí)現(xiàn)決策樹C4.5算法詳解(在ID3基礎(chǔ)上改進(jìn))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
python實(shí)現(xiàn)DEM數(shù)據(jù)的陰影生成的方法
這篇文章主要介紹了python實(shí)現(xiàn)DEM數(shù)據(jù)的陰影生成的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
python實(shí)現(xiàn)外賣信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)外賣信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Python實(shí)現(xiàn)的科學(xué)計(jì)算器功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的科學(xué)計(jì)算器功能,涉及Python基于數(shù)值運(yùn)算與事件響應(yīng)實(shí)現(xiàn)科學(xué)計(jì)算器功能相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
Python?OpenCV實(shí)現(xiàn)任意角度二維碼矯正
這篇文章主要為大家詳細(xì)介紹了如何利用Python?OpenCV實(shí)現(xiàn)任意角度的二維碼快速矯正,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-05-05
python內(nèi)置數(shù)據(jù)類型使用方法和繼承關(guān)系
這篇文章主要介紹了python內(nèi)置數(shù)據(jù)類型使用方法和繼承關(guān)系,介紹內(nèi)容有python包含的內(nèi)置序列、使用內(nèi)置的數(shù)據(jù)類型等相關(guān)資料,需要的小伙伴可以參考一下2022-03-03

