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

评论关闭