9.8 將裝飾器定義為類的一部分

2018-02-24 15:27 更新

問題

你想在類中定義裝飾器,并將其作用在其他函數(shù)或方法上。

解決方案

在類里面定義裝飾器很簡單,但是你首先要確認它的使用方式。比如到底是作為一個實例方法還是類方法。下面我們用例子來闡述它們的不同:

from functools import wraps

class A:
    # Decorator as an instance method
    def decorator1(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('Decorator 1')
            return func(*args, **kwargs)
        return wrapper

    # Decorator as a class method
    @classmethod
    def decorator2(cls, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('Decorator 2')
            return func(*args, **kwargs)
        return wrapper

下面是一使用例子:

# As an instance method
a = A()
@a.decorator1
def spam():
    pass
# As a class method
@A.decorator2
def grok():
    pass

仔細觀察可以發(fā)現(xiàn)一個是實例調(diào)用,一個是類調(diào)用。

討論

在類中定義裝飾器初看上去好像很奇怪,但是在標準庫中有很多這樣的例子。特別的,@property 裝飾器實際上是一個類,它里面定義了三個方法 getter(), setter(), deleter() ,每一個方法都是一個裝飾器。例如:

class Person:
    # Create a property instance
    first_name = property()

    # Apply decorator methods
    @first_name.getter
    def first_name(self):
        return self._first_name

    @first_name.setter
    def first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value

它為什么要這么定義的主要原因是各種不同的裝飾器方法會在關聯(lián)的 property 實例上操作它的狀態(tài)。因此,任何時候只要你碰到需要在裝飾器中記錄或綁定信息,那么這不失為一種可行方法。

在類中定義裝飾器有個難理解的地方就是對于額外參數(shù) selfcls 的正確使用。盡管最外層的裝飾器函數(shù)比如 decorator1()decorator2() 需要提供一個 selfcls 參數(shù),但是在兩個裝飾器內(nèi)部被創(chuàng)建的 wrapper() 函數(shù)并不需要包含這個 self 參數(shù)。你唯一需要這個參數(shù)是在你確實要訪問包裝器中這個實例的某些部分的時候。其他情況下都不用去管它。

對于類里面定義的包裝器還有一點比較難理解,就是在涉及到繼承的時候。例如,假設你想讓在A中定義的裝飾器作用在子類B中。你需要像下面這樣寫:

class B(A):
    @A.decorator2
    def bar(self):
        pass

也就是說,裝飾器要被定義成類方法并且你必須顯式的使用父類名去調(diào)用它。你不能使用 @B.decorator2 ,因為在方法定義時,這個類B還沒有被創(chuàng)建。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號