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