Python descriptor 以及 内置property()函数
Python Descriptor
1, Python Descriptor是这样一个对象
它按照descriptor协议, 有这样的属性之一
def __get__(self, obj, type=None) # 会返回一个value def __set__(self, obj, value) # 返回None def __delete__(self, obj) # 返回None
这样的对象就是一个descriptor
2, descriptor的特性
假若有一个对象t, 我们去引用它的一个属性a
t.a
但是发现a是一个descriptor
那么不会返回a, 而是会去调用a相应的__get__, __set__, __delete__
那么什么情况调用那个呢?如下
- v = t.a <----> v = __get__(a, t)
- t.a = v <-----> __set__(a, t, v)
- del t.a <-----> __delete__(a, t)
3, descriptor是如何实现的
只有new-style objects或class的属性在被引用时,descriptor的特性才能起作用
从 object 派生的类就是 new-style class
class T(object): pass
那么这大概是怎么回事呢?
是因为object有__getattribute__属性, 这个属性的实现确保了descriptor机制
所以如果我们重写了__getattribute__, 那么就可以消除descriptor机制
__getattribute__是如何实现的,以后探讨, 参考2中有一点点例子
内置函数 property()
Python有内置property()函数, 它可以直接做函数,也可以用来做装饰器, 它的使用方式如下, 例子来自参考3
class Test(object): def getx(self): return self._x def setx(self, v): self._x = v def deletex(self): del self._x x = property(getx, setx, deletex, ''' __doc__''')
而上面的代码等价于下面的
class Test(object): @property def x(self): return self._x @x.setter def x(self, v): self._x = v @x.deleter def x(self): del self._x
对于Test的x属性,可以这么用
t = Test() t.x = 5 print t.x del t.x
那么为什么property()可以这么用,尤其是第二种中, x.setter和 x.deleter还可以做装饰器呢?
首先我们要先明白装饰器是什么
property()会返回一个Property对象, 然后我们来看一个用Python模拟的Property类的实现, 摘自参考1
class Property(object): "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__)
认真看看就明白了
- Property对象是Descriptor
- Property.setter 和 Property.deleter 都是装饰器,他们和property一样,都是返回Property()对象,不同的是 @property设置 fget , setter和 deleter分别设置 fset, 和 fdel
-----------------------------------
很好的学习参考:
1, http://stackoverflow.com/questions/17330160/python-how-does-decorator-property-work
2, http://docs.python.org/3.2/howto/descriptor.html
3, http://docs.python.org/3.2/library/functions.html#property