9.12 使用裝飾器擴充類的功能

2018-02-24 15:27 更新

問題

你想通過反省或者重寫類定義的某部分來修改它的行為,但是你又不希望使用繼承或元類的方式。

解決方案

這種情況可能是類裝飾器最好的使用場景了。例如,下面是一個重寫了特殊方法 __getattribute__ 的類裝飾器,可以打印日志:

def log_getattribute(cls):
    # Get the original implementation
    orig_getattribute = cls.__getattribute__

    # Make a new definition
    def new_getattribute(self, name):
        print('getting:', name)
        return orig_getattribute(self, name)

    # Attach to the class and return
    cls.__getattribute__ = new_getattribute
    return cls

# Example use
@log_getattribute
class A:
    def __init__(self,x):
        self.x = x
    def spam(self):
        pass

下面是使用效果:

>>> a = A(42)
>>> a.x
getting: x
42
>>> a.spam()
getting: spam
>>>

討論

類裝飾器通常可以作為其他高級技術比如混入或元類的一種非常簡潔的替代方案。比如,上面示例中的另外一種實現(xiàn)使用到繼承:

class LoggedGetattribute:
    def __getattribute__(self, name):
        print('getting:', name)
        return super().__getattribute__(name)

# Example:
class A(LoggedGetattribute):
    def __init__(self,x):
        self.x = x
    def spam(self):
        pass

這種方案也行得通,但是為了去理解它,你就必須知道方法調(diào)用順序、super() 以及其它8.7小節(jié)介紹的繼承知識。某種程度上來講,類裝飾器方案就顯得更加直觀,并且它不會引入新的繼承體系。它的運行速度也更快一些,因為他并不依賴 super() 函數(shù)。

如果你系想在一個類上面使用多個類裝飾器,那么就需要注意下順序問題。例如,一個裝飾器A會將其裝飾的方法完整替換成另一種實現(xiàn),而另一個裝飾器B只是簡單的在其裝飾的方法中添加點額外邏輯。那么這時候裝飾器A就需要放在裝飾器B的前面。

你還可以回顧一下8.13小節(jié)另外一個關于類裝飾器的有用的例子。

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號