Python中 property 的實現原理及實現純 Python 版

起步

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

setter 函數的實現

這個的實現須要腦子轉個彎。對於修飾符 @x.setter ,由於 x 已是 property() 的實例,因此咱們要完成的 property 要實現 setter 函數,那函數體會是什麼呢?htm

函數體也是要返回描述符對象,並該對象是有 __set__ 的。那 property 不就正好知足嗎,因此這裏的處理方式是 setter 函數會返回一個新的 property 實例。對象

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)

property 的完整實現

這個基本是依據 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 的一個特性,它的內部實現原理很簡單,但在應用上卻很方面,可讀性也十分友好。

相關文章
相關標籤/搜索