Python descriptor 以及 内置property()函数

时间:2022-04-23 13:42:41

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://*.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