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