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 的一个特性,它的内部实现原理很简单,但在应用上却很方面,可读性也十分友好。

相关文章
相关标签/搜索