裝飾器接受一個(gè)函數(shù),添加一些功能并返回它。 在本文中,您將學(xué)習(xí)如何創(chuàng)建裝飾器以及為什么要使用它。
Python有一個(gè)有趣的功能,稱為裝飾器,可將功能添加到現(xiàn)有代碼中。
這也稱為元編程,因?yàn)槌绦虻囊徊糠衷噲D在編譯時(shí)修改程序的另一部分。
為了了解裝飾器,我們必須首先了解Python的一些基本知識(shí)。
我們必須接受這樣一個(gè)事實(shí),即Python中的所有內(nèi)容都是對(duì)象。我們定義的名稱只是綁定到這些對(duì)象的標(biāo)識(shí)符。函數(shù)也不例外,它們也是對(duì)象(帶有屬性)??梢詫⒏鞣N不同的名稱綁定到同一功能對(duì)象。
這是一個(gè)實(shí)例。
def first(msg): print(msg) first("Hello") second = first second("Hello")
當(dāng)您運(yùn)行代碼時(shí),這兩個(gè)函數(shù)first和second給出相同的輸出。在此,名稱first和second指代相同的功能對(duì)象。
現(xiàn)在情況是不是感覺(jué)變復(fù)雜了點(diǎn),可以將函數(shù)作為參數(shù)傳遞給另一個(gè)函數(shù)。
如果您在Python中使用過(guò)map,filter和reduce之類的函數(shù),那么您已經(jīng)知道這一點(diǎn)。
這種以其他函數(shù)為參數(shù)的函數(shù)也稱為高階函數(shù)。這是這種函數(shù)的一個(gè)實(shí)例。
def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result
我們調(diào)用函數(shù)如下。
>>> operate(inc,3) 4 >>> operate(dec,3) 2
此外,一個(gè)函數(shù)可以返回另一個(gè)函數(shù)。
def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() #輸出 "Hello" new()
在這里,is_returned()是一個(gè)嵌套函數(shù),每次我們調(diào)用is_drawn()時(shí),該函數(shù)都會(huì)定義并返回。
最后,我們必須了解Python中的閉包。
函數(shù)和方法被稱為可調(diào)用的,因?yàn)樗鼈兛梢员徽{(diào)用。
實(shí)際上,任何實(shí)現(xiàn)特殊方法__call __()的對(duì)象都稱為可調(diào)用的。 因此,從最基本的意義上講,裝飾器是可調(diào)用的,可返回可調(diào)用的。
基本上,裝飾器接受一個(gè)函數(shù),添加一些功能并返回它。
def make_pretty(func): def inner(): print("我被裝飾了") func() return inner def ordinary(): print("我是普通的函數(shù)")
在shell中運(yùn)行以下代碼時(shí),
>>> ordinary() 我是普通的函數(shù) >>> # 我們來(lái)裝飾一下這個(gè)普通的函數(shù) >>> pretty = make_pretty(ordinary) >>> pretty() 我被裝飾了 我是普通的函數(shù)
在上面顯示的示例中,make_pretty()是一個(gè)裝飾器。在分配步驟中。
pretty = make_pretty(ordinary)
函數(shù)ordinary()被修飾,返回的函數(shù)被命名為pretty。
我們可以看到decorator函數(shù)在原來(lái)的函數(shù)中添加了一些新功能。這類似于包裝禮物。裝飾器充當(dāng)包裝器。被裝飾的物品(里面的禮物)的性質(zhì)不會(huì)改變。但是現(xiàn)在,它看起來(lái)很漂亮(自從裝飾之后)。
通常,我們裝飾一個(gè)函數(shù)并將其重新分配為
ordinary = make_pretty(ordinary).
這是一個(gè)常見(jiàn)的構(gòu)造,因此,Python具有簡(jiǎn)化此語(yǔ)法的語(yǔ)法。
我們可以將@符號(hào)與裝飾器函數(shù)的名稱一起使用,并將其放置在要裝飾的函數(shù)的定義上方。例如,
@make_pretty def ordinary(): print("我是普通的函數(shù)")
相當(dāng)于
def ordinary(): print("我是普通的函數(shù)") ordinary = make_pretty(ordinary)
這只是實(shí)現(xiàn)裝飾器的語(yǔ)法糖。
上面的裝飾器很簡(jiǎn)單,并且只適用于沒(méi)有任何參數(shù)的函數(shù)。如果我們的函數(shù)具有如下所示的參數(shù),該怎么辦?
def divide(a, b): return a/b
此函數(shù)有兩個(gè)參數(shù),a和b。我們知道,如果我們將b傳遞為0 ,則會(huì)產(chǎn)生錯(cuò)誤。
>>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last): ... ZeroDivisionError: division by zero
現(xiàn)在讓我們做一個(gè)裝飾器來(lái)檢查這種情況是否會(huì)導(dǎo)致錯(cuò)誤。
def smart_divide(func): def inner(a,b): print("我要做除法",a,"和",b) if b == 0: print("哎呀!不能除") return return func(a,b) return inner @smart_divide def divide(a,b): return a/b
如果出現(xiàn)錯(cuò)誤情況,此新實(shí)現(xiàn)將返回None。
>>> divide(2,5) 我要做除法 2 和 5 0.4 >>> divide(2,0) 我要做除法 2 和 0 哎呀!不能除
通過(guò)這種方式,我們可以裝飾帶有參數(shù)的函數(shù)。
敏銳的觀察者會(huì)注意到,inner()裝飾器內(nèi)部的嵌套函數(shù)的參數(shù)與其裝飾的函數(shù)的參數(shù)相同??紤]到這一點(diǎn),現(xiàn)在我們可以使通用裝飾器可以使用任意數(shù)量的參數(shù)。
在Python中,此魔術(shù)是通過(guò)完成的function(*args, **kwargs)。這樣,args是位置參數(shù)的元組,kwargs而是關(guān)鍵字參數(shù)的字典。這樣的裝飾器的一個(gè)實(shí)例是。
def works_for_all(func): def inner(*args, **kwargs): print("我可以裝飾任何函數(shù)") return func(*args, **kwargs) return inner
可以在Python中鏈接多個(gè)裝飾器。
這就是說(shuō),一個(gè)函數(shù)可以用不同(或相同)的裝飾器多次裝飾。我們只需將裝飾器放置在所需函數(shù)之上。
def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")
這將給出輸出。
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
上面的語(yǔ)法,
@star @percent def printer(msg): print(msg)
相當(dāng)于
def printer(msg): print(msg) printer = star(percent(printer))
鏈接裝飾器的順序很重要。如果我們按相反的順序,
@percent @star def printer(msg): print(msg)
執(zhí)行將發(fā)生在
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ****************************** Hello ****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%