property
是 Python 內置的功能,經常使用來修飾類方法,用於已訪問屬性的方式調用函數。html
class C(object): def __init__(self): self._x = 'Tom' @property def x(self): return self._x @x.setter def x(self, value): self._x = value c = C() print(c.x) # Tom c.x = 'Tony' print(c.x) # Tony
儘管 property
的實現是 C 實現,但仍不妨礙探究它的實現原理,本文最後也會給出它的純 Python 版本的實現。python
爲了可以實現訪問屬性就調用某個函數,這裏將利用 描述符對象 做爲本文的實現起點,當某個類定義了 __get__
方法後,經過其方法名稱能夠直接調用 __get__
,例如:函數
class Desc: def __init__(self, name): self.name = name def __get__(self, obj, objtype): print('Retrieving', self.name) return self.name class A: x = Desc('Tom') a = A() print(a.x) # 打印了 'Retrieving'
從這點來看,若是咱們自行實現 property
,那它將會是類而不是函數,一樣的爲了可以完成屬性的賦值操做,該類還要設置 __set__
函數。code
這個的實現須要腦子轉個彎。對於修飾符 @x.setter
,由於 x
已是 property()
的實例,因此咱們要完成的 property
要實現 setter
函數,那函數體會是什麼呢?htm
函數體也是要返回描述符對象,並該對象是有 __set__
的。那 property
不就正好知足嗎,因此這裏的處理方式是 setter
函數會返回一個新的 property
實例。對象
基於上述分析,對於開頭中的實例代碼可運行的簡易版本:繼承
class property: def __init__(self, fget=None, fset=None): self.fget = fget self.fset = fset def __get__(self, inst, owner=None): if inst is None: return self return self.fget(inst) def __set__(self, inst, value): self.fset(inst, value) def setter(self, fset): return property(self.fget, fset)
這個基本是依據 C 實現的純 Python 版本,純 C 實如今文件 Objects/descrobject.c
中。ip
Python 實現版本:get
class property: "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None: doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
在建立新的 proptery
實例中使用的是 type(self)(...)
,這是由於考慮到了 proptery
可能被繼承。it
proptery
主要依賴於描述符的機制。proptery
內置也成爲了 Python 的一個特性,它的內部實現原理很簡單,但在應用上卻很方面,可讀性也十分友好。