裝飾器一直以來都是 Python 中很有用、很經(jīng)典的一個(gè) feature滴劲,在工程中的應(yīng)用也十分廣泛攻晒,比如日志、緩存等等的任務(wù)都會(huì)用到班挖。
python中函數(shù)是一等公民鲁捏,使用非常靈活。函數(shù)可以作為參數(shù)萧芙,也可以作為返回值给梅,函數(shù)可以嵌套。
def func_closure():
def get_message(message):
print('Got a message: {}'.format(message))
return get_message
send_message = func_closure()
send_message('hello world')
# 輸出
Got a message: hello world
這里双揪,函數(shù) func_closure() 的返回值是函數(shù)對象 get_message 本身动羽,之后,我們將其賦予變量 send_message盟榴,再調(diào)用 send_message(‘hello world’)曹质,最后輸出了'Got a message: hello world'。
簡單的裝飾器
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
def greet():
print('hello world')
greet = my_decorator(greet)
greet()
# 輸出
wrapper of decorator
hello world
這是簡單裝飾器的例子,greet指向內(nèi)部函數(shù)wrapper羽德,wrapper又會(huì)調(diào)用原函數(shù)greet几莽,因此先輸出'wrapper of decorator',然后輸出'hello world'宅静。
這里的函數(shù) my_decorator() 就是一個(gè)裝飾器章蚣,它把真正需要執(zhí)行的函數(shù) greet() 包裹在其中,并且改變了它的行為姨夹,但是原函數(shù) greet() 不變纤垂。
對于裝飾器,Python中更簡單磷账、優(yōu)雅地使用方式
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
@my_decorator
def greet():
print('hello world')
greet()
帶參數(shù)的裝飾器
def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
args和*kwargs峭沦,表示接受任意數(shù)量和類型的參數(shù)。
自定義參數(shù)的裝飾器
其實(shí)逃糟,裝飾器還有更大程度的靈活性吼鱼。剛剛說了,裝飾器可以接受原函數(shù)任意類型和數(shù)量的參數(shù)绰咽,除此之外菇肃,它還可以接受自己定義的參數(shù)。舉個(gè)例子取募,比如我想要定義一個(gè)參數(shù)琐谤,來表示裝飾器內(nèi)部函數(shù)被執(zhí)行的次數(shù),那么就可以寫成下面這種形式:
def repeat(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
return my_decorator
@repeat(4)
def greet(message):
print(message)
greet('hello world')
# 輸出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
保留原函數(shù)的元信息
通過greet.name可以輸出函數(shù)的元信息玩敏,你會(huì)發(fā)現(xiàn)斗忌,greet() 函數(shù)被裝飾以后,它的元信息變了聊品。元信息告訴我們“它不再是以前的那個(gè) greet() 函數(shù)飞蹂,而是被 wrapper() 函數(shù)取代了”几苍。為了解決這個(gè)問題翻屈,我們通常使用內(nèi)置的裝飾器@functools.wrap,它會(huì)幫助保留原函數(shù)的元信息(也就是將原函數(shù)的元信息妻坝,拷貝到對應(yīng)的裝飾器函數(shù)里)伸眶。
類裝飾器
前面我們主要講了函數(shù)作為裝飾器的用法,實(shí)際上刽宪,類也可以作為裝飾器厘贼。類裝飾器主要依賴于函數(shù)call(),每當(dāng)你調(diào)用一個(gè)類的示例時(shí)圣拄,函數(shù)call()就會(huì)被執(zhí)行一次嘴秸。
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('num of calls is: {}'.format(self.num_calls))
return self.func(*args, **kwargs)
@Count
def example():
print("hello world")
example()
# 輸出
num of calls is: 1
hello world
example()
# 輸出
num of calls is: 2
hello world
Count裝飾器實(shí)現(xiàn)了記錄調(diào)用次數(shù)的功能。
實(shí)際應(yīng)用
def singleton(cls):
_instance = {}
def get_instance():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return get_instance
這是裝飾類的裝飾器,用于實(shí)現(xiàn)單例岳掐。