python裝飾器裝飾類
裝飾器可以用來像裝飾函式一樣裝飾類(畢竟類也是可呼叫物件)
裝飾類可以有多種用途。類裝飾器可以和被裝飾類的屬性互動。一個類裝飾器可以新增或增強屬性,或者它可以修改類的API, 在類如何被定義和它的例項如何被使用之間提供區別。你可能 會問,新增或增強一個類的屬性的合適做法不是通過子類麼?通常,答案是這樣。然而,在某些情況下,備選的方法可能更合適。例如,一個通用的可應用特性可能應用到你的應用的許多類上,在你的類結構的不同地方
舉個例子,考慮類的這樣一種特性,每個例項知道它是什麼時候被例項化的,例項通過建立時間排序。這對許多不同的類有普遍的可應用性。
需要三個額外的屬性—–例項化時間戳, __gt__ 和 __lt__ 方法。
有很多途徑來進行新增. 你可以用一個類裝飾器這樣做:
import functools
import time
def sortable_by_creation_time(cls):
"""Given a class, augment the class to have its instances
be sortable
by the timestamp at which they were instantiated.
"""
# Augment the class' original ‘__init__‘ method to alsostore a
# ‘_created‘ attribute on the instance, which corresponds
#to when it
# was instantiated.
original_init = cls.__init__
@functools.wraps(original_init)
def new_init(self, *args, **kwargs):
original_init(self, *args, **kwargs)
self._created = time.time()
cls.__init__ = new_init
#Add ‘__lt__‘ and ‘__gt__‘ methods that return True or
# False based on
# the created values in question.
cls.__lt__ = lambda self, other: self._created < other._created
cls.__gt__ = lambda self, other: self._created > other._created
# Done; return the class object.
return cls
這個裝飾器做的第一件事情是儲存了類的原始的__init__方法的副本。你不必擔心這個類是否有這個方法。因為物件有__init__ 方法,這個屬性的出現會有保證。下一步,建立一個新方法指派給__init__
這個方法首先呼叫原始的__init__然後做一部分額外工作, 儲存例項的時間戳到 self._created.
值得注意的是這同前面的例子中的執行時包裝程式碼非常相似的模式—-讓一個函式包裝另一個函式,它的主要職責是是執行被包裝函式,但也會新增小片其它功能片段。
如果一個被@sortable_by_creation_time裝飾的類定義了自己的__lt__ 和__gt__方法 ,那麼這個裝飾器會覆蓋它們。
如果這個類沒有意識到created是用來排序的,_created本身來講就沒什麼用處。因此裝飾器也新增 __lt_ 和 __gt__魔法方法.這些引起 < 和 > 操作符基於那些方法的結果返回True或者False。
這也會影響sorted和其它相似函式的行為
這是有必要的所有來讓隨便一個類的例項通過它們的例項化時間可排序。
這個裝飾器可以被應用給任何類。包括許多有不相關的祖先的類。這有一個通過建立時間的可排序例項的簡單類的例子
>>> @sortable_by_creation_time… class Sortable(object):
... def __init__(self, identifier):
... self.identifier = identifier… def _repr_(self):
... return self.identifier…
>>> first = Sortable('first')
>>> second = Sortable('second')
>>> third = Sortable('third')
>>>
>>> sortables = [second, first, third]
>>> sorted(sortables)
[first, second, third]
一定牢記,雖然裝飾器可以解決一個問題,但不意味著它一定就是合適的解決方案。當說到這個例子,同樣的事情能夠通過使用一個“mixin,”或者只定義了合適的__init__, __lt__, 和 __gt__的很小的類來完成。
一個使用了mixin的簡單方法:
import time
class SortableByCreationTime(object):
def __init__(self):
self._created = time.time()
def __lt__(self, other):
return self._created < other._created
def __gt__(self, other):
return self._created > other._created
應用mixin給一個類可以通過使用Python的多繼承來做到:
class MyClass(MySuperclass, SortableByCreationTime):
pass
這個方式有不同的優點和缺點。一方面,它會毫不留情地犁過(plow over)由類或者它的超類(可能以後再閱讀程式碼時,這會不太明顯,裝飾器正在“修理”兩個方法)定義的__lt__ 和 __gt__方法。
另一方面,可能很容易陷入一種境地,由SortableByCreationTime提供的__init__方法不會執行
如果MyClass 或者
MySuperclass 或者任何在MySuperclass的祖先中中定義了一個__init__方法 ,這個方法會勝出。反轉類的順序無法解決這個問題;
相反,裝飾器會很好地處理__init__的情況,只需要增強被裝飾類的__init__方法的影響,否則不做任何變動。
哪種情況是正確的? 視情況而定。