Python中如何理解和使用裝飾器 @decorator,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

Python的裝飾器(decorator)是一個(gè)很棒的機(jī)制,也是熟練運(yùn)用Python的必殺技之一。裝飾器,顧名思義,就是用來裝飾的,它裝飾的是一個(gè)函數(shù),保持被裝飾函數(shù)的原有功能,再裝飾上(添油加醋)一些其它功能,并返回帶有新增功能的函數(shù)對(duì)象,所以裝飾器本質(zhì)上是一個(gè)返回函數(shù)對(duì)象的函數(shù)(確切的說,裝飾器應(yīng)該是可調(diào)用對(duì)象,除了函數(shù),類也可以作為裝飾器)。
在編程過程中,我們經(jīng)常遇到這樣的場(chǎng)景:登錄校驗(yàn),權(quán)限校驗(yàn),日志記錄等,這些功能代碼在各個(gè)環(huán)節(jié)都可能需要,但又十分雷同,通過裝飾器來抽象、剝離這部分代碼可以很好解決這類場(chǎng)景。
要理解Python的裝飾器,首先我們先理解一下Python的函數(shù)對(duì)象。我們知道,在Python里一切都是對(duì)象,函數(shù)也不例外,函數(shù)是第一類對(duì)象(first-class objects),它可以賦值給變量,也可以作為list的元素,還可以作為參數(shù)傳遞給其它函數(shù)。
定義一個(gè)簡(jiǎn)單的函數(shù):
def say_hi():
print('Hi!')
say_hi()# Output: Hi!我們可以通過另外一個(gè)變量say_hi2來引用say_hi函數(shù):
say_hi2 = say_hi print(say_hi2)# Output: <function say_hi at 0x7fed671c4378>say_hi2()# Output: Hi!
上面的語(yǔ)句中say_hi2 和 say_hi 指向了同樣的函數(shù)定義,二者的執(zhí)行結(jié)果也相同。
def say_more(say_hi_func):
print('More')
say_hi_func()
say_more(say_hi)# Output:# More# Hi在上面的例子中,我們把say_hi函數(shù)當(dāng)做參數(shù)傳遞給say_more函數(shù),say_hi 被變量 say_hi_func 引用。
def say_hi():
print('Hi!') def say_name():
print('Tom')
say_name()
say_hi()# Output:# Hi!# Tomsay_name() # 報(bào)錯(cuò)上述代碼中,我們?cè)趕ay_hi()函數(shù)內(nèi)部定義了另外一個(gè)函數(shù)say_name()。say_name()只在say_hi函數(shù)內(nèi)部可見(即,它的作用域在say_hi函數(shù)內(nèi)部),在say_hi外包調(diào)用時(shí)就會(huì)出錯(cuò)。
def say_hi():
print('Hi!') def say_name():
print('Tom') return say_name
say_name_func = say_hi()# 打印Hi!,并返回say_name函數(shù)對(duì)象# 并賦值給say_name_funcsay_name_func()# 打印 Tom上面的例子,say_hi函數(shù)返回了其內(nèi)部定義的函數(shù)say_name的引用。這樣在say_hi函數(shù)外部也可以使用say_name函數(shù)了。
前面我們理解了函數(shù),這有助于我們接下來弄明白裝飾器。
裝飾器是可調(diào)用對(duì)象(callable objects),它用來修改函數(shù)或類。
可調(diào)用對(duì)象就是可以接受某些參數(shù)并返回某些對(duì)象的對(duì)象。Python里的函數(shù)和類都是可調(diào)用對(duì)象。
函數(shù)裝飾器,就是接受函數(shù)作為參數(shù),并對(duì)函數(shù)參數(shù)做一些包裝,然后返回增加了包裝的函數(shù),即生成了一個(gè)新函數(shù)。
讓我們看看下面這個(gè)例子:
def decorator_func(some_func):
# define another wrapper function which modifies some_func
def wrapper_func():
print("Wrapper function started")
some_func()
print("Wrapper function ended")
return wrapper_func # Wrapper function add something to the passed function and decorator returns the wrapper function
def say_hello():
print ("Hello")
say_hello = decorator_func(say_hello)
say_hello()# Output:# Wrapper function started# Hello# Wrapper function ended上面例子中,decorator_func 就是定義的裝飾器函數(shù),它接受some_func作為參數(shù)。它定義了一個(gè)wrapper_func函數(shù),該函數(shù)調(diào)用了some_func但也增加了一些自己的代碼。
上面代碼中使用裝飾器的方法看起來有點(diǎn)復(fù)雜,其實(shí)真正的裝飾器的Python語(yǔ)法是這樣的:
@decorator_funcdef say_hi(): print 'Hi!'
@ 符合是裝飾器的語(yǔ)法糖,在定義函數(shù)say_hi時(shí)使用,避免了再一次的賦值語(yǔ)句。
上面的語(yǔ)句等同于:
def say_hi(): print 'Hi!'say_hi = decorator_func(say_hi)
@a@b@cdef foo():
print('foo')# 等同于:foo = a(b(c(foo)))def decorator_func(some_func):
def wrapper_func(*args, **kwargs):
print("Wrapper function started")
some_func(*args, **kwargs)
print("Wrapper function ended")
return wrapper_func@decorator_func def say_hi(name):
print ("Hi!" + name)上面代碼中,say_hi函數(shù)帶有一個(gè)參數(shù)。通常情況下,不同功能的函數(shù)可以有不同類別、不同數(shù)量的參數(shù),在寫wrapper_func的時(shí)候,我們不確定參數(shù)的名稱和數(shù)量,可以通過*args 和 **kwargs 來引用函數(shù)參數(shù)。
不僅被裝飾的函數(shù)可以帶參數(shù),裝飾器本身也可以帶參數(shù)。參考下面的例子:
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__) return func(*args) return wrapper return decorator@use_logging(level="warn")def foo(name='foo'):
print("i am %s" % name)簡(jiǎn)單來說,帶參數(shù)的裝飾器就是在沒有參數(shù)的裝飾器外面再嵌套一個(gè)參數(shù)的函數(shù),該函數(shù)返回那個(gè)無(wú)參數(shù)裝飾器即可。
前面我們提到裝飾器是可調(diào)用對(duì)象。在Python里面,除了函數(shù),類也是可調(diào)用對(duì)象。使用類裝飾器,優(yōu)點(diǎn)是靈活性大,高內(nèi)聚,封裝性。通過實(shí)現(xiàn)類內(nèi)部的__call__方法,當(dāng)使用 @ 語(yǔ)法糖把裝飾器附加到函數(shù)上時(shí),就會(huì)調(diào)用此方法。
class Foo(object):
def __init__(self, func):
self._func = funcdef __call__(self):
print ('class decorator runing')
self._func() print ('class decorator ending')@Foodef say_hi():
print('Hi!')
say_hi()# Output:# class decorator running# Hi!# class decorator ending使用裝飾器極大地復(fù)用了代碼,但是他有一個(gè)缺點(diǎn)就是原函數(shù)的元信息不見了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看看下面例子:
def decorator_func(some_func):
def wrapper_func(*args, **kwargs):
print("Wrapper function started")
some_func(*args, **kwargs)
print("Wrapper function ended")
return wrapper_func@decorator_func def say_hi(name):
'''Say hi to somebody'''
print ("Hi!" + name)
print(say_hi.__name__) # Output: wrapper_funcprint(say_hi.__doc__) # Output: None可以看到,say_hi函數(shù)被wrapper_func函數(shù)取代,它的__name__ 和 docstring 也自然是wrapper_func函數(shù)的了。
不過不用擔(dān)心,Python有functools.wraps,wraps本身也是一個(gè)裝飾器,它的作用就是把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息。
from functools import wrapsdef decorator_func(some_func): @wraps(func)
def wrapper_func(*args, **kwargs):
print("Wrapper function started")
some_func(*args, **kwargs)
print("Wrapper function ended")
return wrapper_func@decorator_func def say_hi(name):
'''Say hi to somebody'''
print ("Hi!" + name)
print(say_hi.__name__) # Output: say_hiprint(say_hi.__doc__) # Output: Say hi to somebody類屬性@property
靜態(tài)方法@staticmethod
類方法@classmethod
通常,我們需要先實(shí)例化一個(gè)類的對(duì)象,再調(diào)用其方法。
若類的方法使用了@staticmethod或@classmethod,就可以不需要實(shí)例化,直接類名.方法名()來調(diào)用。
從使用上來看,@staticmethod不需要指代自身對(duì)象的self或指代自身類的cls參數(shù),就跟使用普通函數(shù)一樣。@classmethod不需要self參數(shù),但第一個(gè)參數(shù)必須是指代自身類的cls參數(shù)。如果在@staticmethod中要調(diào)用到這個(gè)類的一些屬性方法,只能直接類名.屬性名,或類名.方法名的方式。
而@classmethod因?yàn)槌钟衏ls參數(shù),可以來調(diào)用類的屬性,類的方法,實(shí)例化對(duì)象等。
通過認(rèn)識(shí)Python的函數(shù),我們逐步弄清了裝飾器的來龍去脈。裝飾器是代碼復(fù)用的好工具,在編程過程中可以在適當(dāng)?shù)膱?chǎng)景用多多使用。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。
網(wǎng)站標(biāo)題:Python中如何理解和使用裝飾器@decorator-創(chuàng)新互聯(lián)
轉(zhuǎn)載注明:http://www.chinadenli.net/article0/ipsoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、外貿(mào)建站、移動(dòng)網(wǎng)站建設(shè)、定制網(wǎng)站、ChatGPT、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容