在 Python 衆多原生特性中,描述符多是最少被自定義的特性之一,但它在底層實現的方法和屬性卻無時不刻被使用着,它優雅的實現方式體現出 Python 簡潔之美。html
__get__
, __set__
或者 __delete__
任一方法的類稱爲描述符類,其實例對象即是一個描述符,這些方法稱爲描述符協議。obj.__dict__
→ type(obj).__dict__
→ type(obj)的父類.__dict__
順序進行查找,若是查找到目標屬性並發現是一個描述符,Python 會調用描述符協議來改變默認的控制行爲。@property
@classmethod
@staticmethod
和 super
的底層實現機制。__get__
和 __set__
的描述符稱爲 數據描述符(data descriptor);僅定義了 __get__
的稱爲 非數據描述符(non-data descriptor) 。二者區別在於:若是 obj.__dict__
中有與描述符同名的屬性,若描述符是數據描述符,則優先調用描述符,如果非數據描述符,則優先使用 obj.__dict__
中屬性。__get__(self, instance, owner)
程序員
_:param self: _描述符對象自己編程
_:param instance: _使用描述符的對象的實例緩存
_:param owner: _使用描述符的對象擁有者微信
__set__(self, instance, value)
併發
_:param value: _對描述符的賦值ide
__delete__(self, instance)
ui
class LazyProperty(object): """ 實現惰性求值(訪問時才計算,並將值緩存) 利用了 obj.__dict__ 優先級高於 non-data descriptor 的特性 第一次調用 __get__ 以同名屬性存於實例字典中,以後就再也不調用 __get__ """ def __init__(self, fun): self.fun = fun def __get__(self, instance, owner): if instance is None: return self value = self.fun(instance) setattr(instance, self.fun.__name__, value) return value class ReadonlyNumber(object): """ 實現只讀屬性(實例屬性初始化後沒法被修改) 利用了 data descriptor 優先級高於 obj.__dict__ 的特性 當試圖對屬性賦值時,總會先調用 __set__ 方法從而拋出異常 """ def __init__(self, value): self.value = value def __get__(self, instance, owner): return self.value def __set__(self, instance, value): raise AttributeError( "'%s' is not modifiable" % self.value ) class Circle(object): pi = ReadonlyNumber(3.14) def __init__(self, radius): self.radius = radius @LazyProperty def area(self): print('Computing area') return self.pi * self.radius ** 2
https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.htmlcode
編程思惟不該只存留在代碼之中,更應伴隨於整我的生旅途,這個公衆號不僅聊技術,還會聊產品/互聯網/經濟學等普遍話題,因此也歡迎非程序員關注。htm