裝飾器(decorators)是python的乙個重要部分。簡單地說:他們是修改其它函式的功能的函式。他們有助於讓我們的**更簡短,也更pythonic。
一切皆物件
我們可以將乙個函式賦值給乙個變數
def hi():
print("hihi")
a=hi
print(a,type(a))
hi() #呼叫hi函式,需要加()
執行結果
在函式中定義函式
def hi():
print("hihi")
def yun():
return "yunyun"
print(yun())
hi()
#yun()#報錯#在hi()函式之外是不能訪問的
執行結果
從函式中返回函式
def hi(name="xx"):
print(name)
def he():
return "xixi"
return he #返回he相當於返回he()函式
a=hi() #此時不會呼叫he(),因為hi()函式中無呼叫此函式的語句
print(a,type(a))
print(a()) #函式呼叫需要加()
執行結果
將函式作為引數傳給另乙個函式
def hi(name="xx"):
print(name)
def study(a):
a()print("我是study")
study(hi)
執行結果
第乙個裝飾器
下面的**你看的懂?都是上面剛剛提到的知識點。
def aa(bb):
print("我是aa哈")
def cc():
print("我是cc吆")
bb()
return "cc"
return cc
def xixi():
print("我是xixi")
m=aa(xixi) #輸出:我是aa哈,
print(m,type(m)) #m是cc()函式
print(m()) #我是cc吆,我是xixi,cc函式的返回值
執行結果
下面我們使用@來修改上面的**
def aa(bb):
print("我是aa哈")
def cc():
print("我是cc吆")
bb()
return "cc"
return cc
@aadef xixi():
print("我是xixi")
print(xixi()) #等價於aa(xixi)()
執行結果
下面存在乙個問題
print(xixi.__name__) #輸出cc
這並不是我們想要的!應該輸出xixi。這裡的函式被cc替代了。它重寫了我們函式的名字和注釋文件(doctring)。幸運的是python提供給我們乙個簡單的函式來解決這個問題,那就是functools.wraps。我們使用functools.wraps修改上乙個例子
from functools import wraps
def aa(bb):
print("我是aa哈")
@wraps(bb)
def cc():
print("我是cc吆")
bb()
return "cc"
return cc
@aadef xixi():
print("我是xixi")
#print(xixi()) #等價於aa(xixi)()
print(xixi.__name__)
執行結果
注意:@wraps接受乙個函式來進行裝飾,並加入了複製函式名稱、注釋文件、引數列表等等的功能。這可以讓我們在裝飾器裡面訪問在裝飾之前的函式的屬性。
藍本規範:
from functools import wraps
def aa(bb):
print("我是aa哈")
@wraps(bb)
def cc(*args,**kwargs):
if not can_run:
return "bb方法不執行"
return bb(*args,**kwargs)
return cc
@aadef xixi():
return "我是xixi"
can_run=false
print(xixi()) #我是aa哈;bb方法不執行;
# can_run=true
# print(xixi()) #我是aa哈;我是xixi
使用場景
授權(authorization)
日誌(logging)
帶引數的裝飾器
來想想這個問題,難道@wraps不也是個裝飾器嗎?但是,它接收乙個引數,就像任何普通的函式能做的那樣。那麼,為什麼我們不也那樣做呢?這是因為,當你使用@aa語法時,你是在應用乙個以單個函式作為引數的乙個包裹函式。記住,python裡每個東西都是乙個物件,而且這包括函式!
在函式中嵌入裝飾器
from functools import wraps
def aa(name="xiaoxiao"):
def bb(cc):
@wraps(cc)
def dd(*args,**kwargs):
log=cc.__name__+"被呼叫"
print(log)
return cc(*args,**kwargs)
return dd
return bb
# @aa() #注意與無參的對比,這裡多了()
# def xixi():
# return "我是xixi"
@aa(name="dehua")
def xixi():
return "我是xixi"
print(xixi()) #xixi被呼叫,我是xixi
執行結果
裝飾器類
沒錯,裝飾器不僅可以是函式,還可以是類,相比函式裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器主要依靠類的__call__方法,當使用@形式將裝飾器附加到函式上時,就會呼叫此方法。
class foo(object):
def __init__(self, func):
self._func = func
#print(self._func)
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@foo
def bar():
print ('bar')
bar()
執行結果
python 帶引數裝飾器
在前面一文 python裡為什麼需要使用裝飾器 decorator 裡,我們學習了為什麼需要裝飾器,知道裝飾器就是為了不修改原來函式的 又達到增加功能的作用。其實為了裝飾器更通用化,那麼裝飾器是否也可以帶引數呢?其實是可以的,這樣更加通用化了,達到共享極點。在前面也學習 為什麼要使用閉包 closu...
無參裝飾器函式和帶參裝飾器函式
python裝飾器 下邊幾個裝飾器帶引數和不帶引數例子詳解 我們都知道,python中函式是可以被當做引數進行傳遞的,所以最直接的裝飾器例項如下 def decorator func 裝飾器函式 print welcome func print end def test print hello wo...
帶函式的裝飾器 多個裝飾器裝飾乙個函式
一 帶引數的裝飾器 開關 author administrator f true defouter f def wap fun ggdef inner args,kwargs iff print inner before ret fun args,kwargs gg print inner afte...