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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM