在理解類裝飾器之前,先回憶一下有關裝飾器的知識。裝飾器本質上就是一個函式,它可以讓其他函式在不需要做任何程式碼變動的前提下增加額外的功能,裝飾器的返回值也是一個函式物件(函式的引用)。
__call__方法
一個物件是否可呼叫,看其中是否實現了__call__方法。例如,類的物件是不可呼叫的,因為其中沒有實現__call__方法。如果我們在一個類中定義了__call__方法,那麼這個類物件將變得可呼叫。只要某個物件定義了__call__()方法,那麼這個物件就是callable的。
類中沒有實現__call__方法: >>> class A: # 類中沒有實現__call__方法 ... pass >>> a = A() >>> a() # 類物件不可呼叫 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: `A` object is not callable 類中實現了__call__方法: >>> class B: ... def __init__(self): ... self.name = `zhangsan` ... def __call__(self): # 類中實現了__call__方法 ... print(`類中實現了__call__方法:`, self.name) ... >>> b = B() >>> b() # 類物件可呼叫,並在呼叫類物件的時候,自動執行__call__方法 類中實現了__call__方法: zhangsan
類裝飾器
類裝飾器本質上和函式裝飾器原理、作用相同,都是為其它函式增加額外的功能。但是相比於函式裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器可以直接依靠類內部的__call__方法來實現,當使用 @ 形式將類裝飾器附加到函式上時,就會呼叫類裝飾器的__call__方法。而不需要向函式裝飾器那樣,在裝飾器函式中定義巢狀函式,來實現裝飾功能。
使用類裝飾器為一個函式的執行增加計時功能。
>>> import time >>> class Foo(): ... def __init__(self, func): # 初始化函式中傳入函式物件的引數 ... self._func = func ... def __call__(self): # 定義__call__方法,直接實現裝飾功能 ... start_time = time.time() ... self._func() ... end_time = time.time() ... print(`花費了 %.2f` % (end_time - start_time)) ... >>> @Foo # bar=Foo(bar) ... def bar(): ... print(`bar函式的執行時間為:`) ... time.sleep(2.5) ... >>> bar() # bar=Foo(bar)(),沒有巢狀關係了,直接執行Foo的 __call__方法,實現裝飾功能 bar函式的執行時間為: 花費了 2.51
上面的@Foo相當於bar = Foo(bar),等號左邊的bar是類Foo的例項物件,等號右邊的bar是類裝飾器所裝飾的函式名bar作為引數傳遞給類Foo。下面執行bar()即為呼叫類Foo的物件bar,此時會自動呼叫類中定義的__call__方法。