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

2018-02-24 15:27 更新

問題

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

解決方案

在類里面定義裝飾器很簡單,但是你首先要確認(rèn)它的使用方式。比如到底是作為一個(gè)實(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

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

討論

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

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

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

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

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

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

也就是說,裝飾器要被定義成類方法并且你必須顯式的使用父類名去調(diào)用它。你不能使用 @B.decorator2 ,因?yàn)樵诜椒ǘx時(shí),這個(gè)類B還沒有被創(chuàng)建。

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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)