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

 

posted on 2014-03-15 13:40  真实的活  阅读(3656)  评论(1编辑  收藏

导航