Python中實例的attributes、Properties以及Descriptors

在很多的語言中,實例的屬性都有對應的實例變量與之對應,但在Python中,還可以使用其他的方式:

  1. Properties:

    即通過使用Python中內置方法property爲一個Attrbute名綁定對應的getter、setter、deletter方法,或者通過@property裝飾器,這樣,就可以直接通過變量名對實例變量進行訪問。

  2. Descriptors:
    一個描述器是一個具有綁定行爲的對象屬性,其訪問控制被描述器協議重寫。這些方法包括__get__(), __set__(), 和 __delete__()方法,只要重寫了這三個方法中的任何一個,就是實現了描述器協議。

1. 直接通過實例變量訪問

通常情況下,都是直接通過instance.variablename來對實例中的對象進行訪問。

例子1:

>>> class A(object):
...     pass
... 
>>> a = A()
>>> dir(a)
['__class__', '__delattr__', '__dict__', '__doc__',
 '__format__', '__getattribute__', '__hash__', '__init__', 
 '__module__', '__new__', '__reduce__', '__reduce_ex__', 
 '__repr__', '__setattr__', '__sizeof__', '__str__', 
 '__subclasshook__', '__weakref__']

>>> a.__dict__
{}
>>> a.attr1 = 1
>>> dir(a)
['__class__', '__delattr__', '__dict__', '__doc__', 
'__format__', '__getattribute__', '__hash__', '__init__', 
'__module__', '__new__', '__reduce__', '__reduce_ex__', 
'__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', '__weakref__', 'attr1']

>>> a.__dict__
{'attr1': 1}
>>>

從上面的例子中,可以看出,直接對實例添加實例變量,是直接在實例中__dict__添加的。因此,這種是直接進行訪問。

2. 通過Property

property是一種創建數據描述器的簡潔方式,使得在訪問屬性時,會對觸發對應的方法調用。

class C(object):
    def getx(self):
        print 'getx'
        return self.__x

    def setx(self, value):
        print 'setx'
        self.__x = value

    def delx(self):
        print 'delx'
        del self.__x
       x = property(getx, setx, delx, "I'm the 'x' property.")

instances = C()
instances.x = 5
print instances.x
del instances.x

output:-----
setx
getx
5
delx

在上面的代碼中,如果將x = property(getx, setx, delx, "I'm the 'x' property.")換成x = property(fget=getx, fdel=delx, doc="I'm the 'x' property."),那麼調用instances.x會報錯AttributeError: can't set attribute.

3. 通過Descriptors

Descriptor只對新式類有效

如果一個對象定義了__set____get__兩個方法,那可以成爲是一個data-descriptor,如只有__get__,那麼被成爲non-data descriptor。在data-descriptor中,訪問屬性時,優先使用data-descriptor,而在non-data descriptor中優先使用的是字典中的屬性。

技巧: 如果想做一個只讀的descriptor,那麼就可以同時定義 __get__()and __set__() , 其中 調用__set__()時拋出AttributeError異常即可。

3.1 調用順序

  1. __getattribute__()
  2. data-descriptor
  3. 實例字典
  4. non-data descriptor
  5. __getattr__,處理查詢不到的屬性。

這裏可以做很多很多文章。後面進行整理。

data-descriptor 查詢順序:類-基類-實例字典
non-data descriptor 查詢順序:實例字典-類-基類

3.2 示例

class RevealAccess(object):
    """A data descriptor that sets and returns values
       normally and prints a message logging their access.
    """

    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print 'Retrieving', self.name
        return self.val

    def __set__(self, obj, val):
        print 'Updating', self.name
        self.val = val

>>> class MyClass(object):
...     x = RevealAccess(10, 'var "x"')
...     y = 5
...
>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5

重點:

  • descriptors方法被 __getattribute__()方法調用,每次訪問都會進行調用__getattribute__()方法。
  • 重寫 __getattribute__()可以阻止系統子自帶的descriptor調用
  • __getattribute__() 只能被新式的類和實例有效,對於類和實例,調用方法object.__getattribute__() and type.__getattribute__()時,對調用 __get__()方法是不一樣的.
  • data descriptors優先於實例字典。
  • non-data descriptors 可能被實例字典重寫,因爲實例字典的優先級總是高於non-data descriptors。
  • descriptor的實例自己訪問自己是不會觸發get,而會觸發call,只有descriptor作爲其它類的屬性纔有意義。

參考文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章