裝飾器微妙之談
簡單的裝飾器
def my_decorate(fun):????def?wrapper():????????print('這是個裝飾器!')????????func()?????reture?wapper?????def greet():print('hello world')greet = my_decorator(greet)greet()# 輸出這是個裝飾器!hello world
這段代碼中變量 greet 指向了內(nèi)部函數(shù) wrapper(),而內(nèi)部函數(shù) wrapper() 中又會調(diào)用原函數(shù) greet(),因此,最后調(diào)用 greet() 時,就會先打印 'wrapper of decorator',然后輸出'hello world'。大家應(yīng)該都明白!
這里的函數(shù) my_decorator() 就是一個裝飾器,它把真正需 要執(zhí)行的函數(shù) greet() 包裹在其中,并且改變了它的行為, 但是原函數(shù) greet() 不變。
我們換一種寫法,使用Python來裝飾它
def my_decorator(func):def wrapper():print('wrapper of decorator')func()??return wrapperdef?greet():????print('hello?world')greet()
這里的@,我們稱之為語法糖,@my_decorator就相當(dāng)于 前面的greet=my_decorator(greet)語句,只不過更加 簡潔。因此,如果你的程序中有其它函數(shù)需要做類似的裝飾,你只需在它們的上方加上@decorator就可以了,這樣 就大大提高了函數(shù)的重復(fù)利用和程序的可讀性。
帶有參數(shù)的裝飾器
如果原函數(shù) greet() 中,有參數(shù)需要傳遞給 裝飾器怎么辦?一個簡單的辦法,是可以在對應(yīng)的裝飾器函數(shù) wrapper() 上,加上相應(yīng)的參數(shù),比如:
def my_decorator(func):def?wrapper(message):print('wrapper of decorator')func(message)return wrapperdef greet(message):print(message)greet('hello world')# 輸出wrapper of decoratorhello world
事實(shí)上,通常情況下,我們會把*args和**kwargs,作為 裝飾器內(nèi)部函數(shù) wrapper() 的參數(shù)。
def my_decorator(func):def wrapper(*args, **kwargs):print('wrapper of decorator')func(*args, **kwargs)return wrapper
類裝飾器
實(shí)際上,類也可 以作為裝飾器。類裝飾器主要依賴于函數(shù)__call_(),每當(dāng)你調(diào)用一個類的示例時,函數(shù)__call__() 就會被執(zhí)行一 次。
class Count:def __init__(self, func):self.func = funcself.num_calls = 0def __call__(self, *args, **kwargs):self.num_calls += 1print('num of calls is: {}'.format(self.num_calls))return self.func(*args, **kwargs)@Countdef example():print("hello world")example()# 輸出num of calls is: 1hello worldexample()# 輸出num of calls is: 2hello world
?這里,定義了類 Count,初始化時傳入原函數(shù) func(), 而__call__() 函數(shù)表示讓變量 num_calls 自增 1,然后打印,并且調(diào)用原函數(shù)。因此,在我們第一次調(diào)用函數(shù) example() 時,num_calls 的值是 1,而在第二次調(diào)用時, 它的值變成了 2。
