本文是裝飾器相關內容的第二篇,關於類裝飾器。html
"類裝飾器"有兩種解讀方式:用來裝飾類的裝飾器;類做爲裝飾器裝飾其它東西。你如何認爲取決於你,兩種說法都有出如今其它的文章中。個人文章中是將"類裝飾器"解讀爲第一種方式,即裝飾類的東西。而「類做爲裝飾器裝飾其它東西」,我都會爲其標註"類做爲裝飾器"或"做爲裝飾器的類"以免歧義。app
函數裝飾器是裝飾函數(方法)的,類裝飾器是裝飾類的,它們的表現形式是同樣的。函數
@decorator class cls: ... c = cls()
等價於:code
class cls: ... cls = decorator(cls) c = cls()
它的效果是建立實例對象的時候,會觸發裝飾器中的代碼邏輯。htm
再細細一想,發現decorator(cls)
要返回的是一個類,因此decorator中的結構大概是這樣的:對象
def decorator(cls): class wrapper: ... return wrapper
這樣就會讓被包裝的類cls實際變成wrapper類,而且之後調用cls構造對象的時候,其實是調用wrapper類來構造對象。換句話說,wrapper已經攔截了對全部的cls操做。blog
但並不是必定如此,好比直接返回原始的cls:get
def decorator(cls): ...do something about cls... return cls
這種方式比較簡單,本文主要對前一種方式進行詳細解釋。it
因爲返回的是class wrapper
,那麼它裝飾類的時候,假設所裝飾的類有構造方法__init__
,構造方法中有屬性,這個類中還有方法。以下:class
@decorator class cls(): # 等價於cls = decorator(cls) def __init__(self, x, y): self.attrx = x self.attry = y def method(self): return self.x, self.y
那麼在包裝器wrapper中,須要可以構造出這個對象,而且可以取得被包裝類的對象屬性、類屬性。以下:
def decorator(cls): class wrapper(): def __init__(self, *args, **kwargs): self.wrapped = cls(*args, **kwargs) def __getattr__(self, name): return getattr(self.wrapped, name) return wrapper
由於操做cls類的時候,其實是在操做wrapper類。因此構造cls對象的時候:
c = cls(3, 4)
其實是在調用wrapper(3, 4)
來構造對象,因此會執行wrapper裏的__init__
。但類裝飾器最終的目標是爲了擴展類cls,因此在wrapper裏必須得構造出cls的對象。上面採起的方式是經過cls()來構造cls對象,並放在wrapper對象的一個屬性wrapped中。
由於cls已經被金蠶脫殼成了wrapper,因此要獲取到cls的屬性必須在wrapper中重寫屬性獲取的方式。
下面是一個示例:
def decorator(cls): class wrapper(): def __init__(self, *args, **kwargs): self.wrapped = cls(*args, **kwargs) def __getattr__(self, name): return getattr(self.wrapped, name) return wrapper @decorator class cls(): def __init__(self, x, y): self.attrx = x self.attry = y def method(self): return self.attrx, self.attry c = cls(3, 4) print(c.attrx) print(c.attry) print(c.method())
輸出結果:
3 4 (3, 4)