一、前言
python裝飾器本質(zhì)上就是一個(gè)函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外的功能,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象。
Python中一切皆可對(duì)象
二、函數(shù)帶和不帶()的區(qū)別
def test():
print('test ist running')
test()
print(test)
test_two = test
test_two()
#輸出
test ist running
<function test at 0x0000021ED072C268>
test ist running
如代碼所示,test()和test輸出是不同的,這是因?yàn)楫?dāng)你把一對(duì)小括號(hào)放在后面,這個(gè)函數(shù)就會(huì)執(zhí)行;然而如果你不放括號(hào)在它后面,它是一個(gè)對(duì)象,那它可以被到處傳遞,并且可以賦值給別的變量而不去執(zhí)行它
三、簡(jiǎn)單的裝飾器
def use_logging(func):
def wrapper():
"""wrapper()"""
print('%s start running' % func.__name__)
f = func()
print('%s stop running' % func.__name__)
return f
return wrapper
@use_logging
def foo():
"""foo()"""
print("i am foo")
foo()
#output
foo start running
i am foo
foo stop running
or(代碼稍微不同,可以控制執(zhí)行順序吧)
2024-05-22改
上面的f = func()會(huì)執(zhí)行函數(shù),所以順序變了
def use_logging(func):
def wrapper():
"""wrapper()"""
print('%s start running' % func.__name__)
#f = func()
print('%s stop running' % func.__name__)
return func()
return wrapper
@use_logging
def foo():
"""foo()"""
print("i am foo")
foo()
#output
foo start running
foo stop running
i am foo
use_logging 就是一個(gè)裝飾器,它是一個(gè)普通的函數(shù),它把執(zhí)行真正業(yè)務(wù)邏輯的函數(shù) func 包裹在其中,看起來(lái)像 foo 被 use_logging 裝飾了一樣,use_logging 返回的也是一個(gè)函數(shù),這個(gè)函數(shù)的名字叫 wrapper。
如果業(yè)務(wù)函數(shù)帶有參數(shù),且不確定有幾個(gè)參數(shù),這是就需要用*args,**kwargs兩兄弟了。
def use_logging(func):
def wrapper(*args, **kwargs):
"""wrapper()"""
print('%s start running' % func.__name__)
#f = func(*args, **kwargs)
print('%s stop running' % func.__name__)
return func(*args, **kwargs)
return wrapper
六、warps
__name__用于獲取函數(shù)的名稱,__doc__用于獲取函數(shù)的docstring內(nèi)容(函數(shù)的注釋)
在上面章節(jié)的示例代碼加入如下代碼
print(foo.__name__)
print(foo.__doc__)
#output
wrapper
wrapper()
可以發(fā)現(xiàn)foo函數(shù)的名稱和注釋都被裝飾器的wrapper函數(shù)覆蓋了。
Python提供了一個(gè)簡(jiǎn)單函數(shù)解決了這個(gè)問(wèn)題。
from functools import wraps
def use_logging(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""wrapper()"""
print('%s start running' % func.__name__)
#f = func(*args, **kwargs)
print('%s stop running' % func.__name__)
return func(*args, **kwargs)
return wrapper
@use_logging
def foo():
"""foo()"""
print("i am foo")
print(foo.__name__)
print(foo.__doc__)
#output
foo
foo()
@wraps接受一個(gè)函數(shù)來(lái)進(jìn)行裝飾,并加入了復(fù)制函數(shù)名稱、注釋文檔、參數(shù)列表等等的功能。這可以讓我們?cè)谘b飾器里面訪問(wèn)在裝飾之前的函數(shù)的屬性。
五、裝飾器帶參數(shù)
import logging
from functools import wraps
def use_logging(level=‘warn’):
@wraps(func)
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warning("%s is running" % func.__name__)
elif level == "info":
logging.info("%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)
foo()
上面的 use_logging是允許帶參數(shù)的裝飾器。它實(shí)際上是對(duì)原有裝飾器的一個(gè)函數(shù)封裝,并返回一個(gè)裝飾器。我們可以將它理解為一個(gè)含有參數(shù)的閉包。當(dāng)我 們使用@use_logging(level="warn")調(diào)用的時(shí)候,Python 能夠發(fā)現(xiàn)這一層的封裝,并把參數(shù)傳遞到裝飾器的環(huán)境中。
@use_logging(level="warn") 等價(jià)于 @decorator
六、類裝飾器
關(guān)于call方法,不得不先提到一個(gè)概念,就是可調(diào)用對(duì)象(callable),我們平時(shí)自定義的函數(shù)、內(nèi)置函數(shù)和類都屬于可調(diào)用對(duì)象,
但凡是可以把一對(duì)括號(hào)()應(yīng)用到某個(gè)對(duì)象身上都可稱之為可調(diào)用對(duì)象,判斷對(duì)象是否為可調(diào)用對(duì)象可以用函數(shù) callable。
如果在類中實(shí)現(xiàn)了call方法,那么實(shí)例對(duì)象也將成為一個(gè)可調(diào)用對(duì)象
import time
class runtime(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start = time.time()
f = self.func(*args, **kwargs) # 原函數(shù)
end = time.time()
print("運(yùn)行時(shí)長(zhǎng):%.4f 秒" % (end-start))
return f
@runtime
def func_a(a):
print("hello"+a)
time.sleep(0.5)
@runtime
def func_b(b, c="xx"):
print("world"+b+c)
time.sleep(0.8)
if __name__ == '__main__':
func_a("a")
func_b("b", c="xxx")
類裝飾器帶參數(shù)
import time
class runtime(object):
def __init__(self, slowly=1):
self.slowly = slowly
def __call__(self, func):
def wrapper(*args, **kwargs):
start = time.time()
f = func(*args, **kwargs) # 原函數(shù)
end = time.time()
t = end-start
time.sleep((self.slowly-1)*t) # 延遲效果
new_end = time.time()
print("運(yùn)行時(shí)長(zhǎng):%.4f 秒" % (new_end-start))
return f
return wrapper
@runtime(1.5)
def func_a(a):
print("hello"+a)
time.sleep(0.5)
@runtime(1.5)
def func_b(b, c="xx"):
print("world"+b+c)
time.sleep(0.8)
if __name__ == '__main__':
func_a("a")
func_b("b", c="xxx")
參考:
菜鳥(niǎo)教程
上海-悠悠