python描述符理解


Python中的描述符是一個相對底層的概念

descriptor
Any object which defines the methods get(), set(), or delete(). When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, using a.b to get, set or delete an attribute looks up the object named b in the class dictionary for a, but if b is a descriptor, the respective descriptor method gets called. Understanding descriptors is a key to a deep understanding of Python because they are the basis for many features including functions, methods, properties, class methods, static methods, and reference to super classes. For more information about descriptors’ methods, see Implementing Descriptors.

描述符
任何實現了__get__(), __set__(), 或者 __delete__()方法的對象就是描述符。一個class的屬性是一個描述符的時候,對這個屬性的訪問會觸發特定的綁定行為。一般的我們使用a.b的方式訪問,修改和刪除屬性,它通過查找class的字典來訪問屬性b,當時如果b是一個描述符,那么get,set和delete相關的描述符方法會被調用。理解描述符是深入理解python的關鍵,因為描述符是很多特性實現的基礎,比如:方法,函數,屬性,類方法,靜態方法還有對父類方法的引用。詳細的說明請看描述符的實現。--CooMark譯

極客學院 - 描述符
Python方法綁定——Unbound/Bound method object的一些梳理
http://www.it165.net/pro/html/201406/15171.html - 最后總結的很好

在說描述符之前,先看一小段代碼:

class Foo(object):
    """docstring for Foo"""

    def __init__(self, arg=''):
        super(Foo, self).__init__()
        self.arg = arg

    def foo(self):
        print(self)
        print('foo:', 123)


print(Foo.foo)

print(Foo().foo)

print(Foo.foo.__get__)

# 輸出
# <function Foo.foo at 0x00550738>
# <bound method Foo.foo of <__main__.Foo object at 0x0054DB10>>
# <method-wrapper '__get__' of function object at 0x00550738>

主要想說的是這個__get__,他是一個method-wrapper。每次調用一個方法,其實都是調用這個__get__構造的method對象。我們定義的方法其實都是descriptor,它通過__get__控制了對相應method的訪問

def __get__(self, instance, owner)
Foo.foo -- Foo.__dict__['foo'].__get__(None,Foo) # 隱式傳遞的self是Foo class對象, 一切皆對象,class在模塊級別中也有instance

下面三種方式的調用輸出的結果是一樣的,這個None咋來的呢?,有一點可以確認:self都是class對象實例

# 兩種訪問方式是一樣的
print(Foo.foo.__get__(Foo(), Foo)())
print(Foo.__dict__['foo'].__get__(Foo(), Foo)())

c = Foo()
print(Foo.foo.__get__(c, Foo)())

# <__main__.Foo object at 0x0032DB10>
# foo: 123
# None
# <__main__.Foo object at 0x0032DB10>
# foo: 123
# None
# <__main__.Foo object at 0x0032DB70>
# foo: 123
# None

再說說bound method和unbound method

區分依據就是是否給method綁定了實例對象,也就是調用的時候是否會隱式傳遞self參數

print(Foo.foo)
# 當時3.0之后不再叫unbound method,因為定義為function更貼切
# <function Foo.foo at 0x006406F0>

print(Foo().foo)
# <bound method Foo.foo of <__main__.Foo object at 0x0063DB30>>

描述符

我們定義的屬性,方法其實都是描述符,只不過我們習以為常,而沒有刻意的去了解背后的機制

class Bar(object):
    """docstring for Bar"""
    _name = 'Mark Xiao'

    def __init__(self, arg=''):
        super(Bar, self).__init__()
        self.arg = arg
        self._name = 'CooMark'

    def _get_name(self):
        return self._name

    def _set_name(self, value):
        self._name = value

    def _del_name(self):
        del self._name

    name = property(_get_name, _set_name, _del_name, 'description of property name')

print(Bar.name)
print(Bar().name)

# <property object at 0x004FBAE0>
# CooMark

描述符協議:

__get__(self, instance, owner) --> return value
__set__(self, instance, value)
__delete__(self, instance)

描述符對象以類型 (owner class) 成員的方式出現,且最少要實現一個協議方法。最常見的描述符有 property、staticmethod、classsmethod。訪問描述符類型成員時,解釋器會自動調用與行為相對應的協議方法。

  1. 實現 get 和 set 方法,稱為 data descriptor。
  2. 僅有 get 方法的,稱為 non-data descriptor。
  3. get 對 owner_class、owner_instance 訪問有效。
  4. set、delete 僅對 owner_instance 訪問有效。

instance method, class method, static method

實例方法bound到instance
類方法bound到class
靜態方法沒有綁定,僅僅是個方法

總結

描述符是一種協議,實現了相應的描述符方法便會有相應的描述符行為,property就是一個特定的描述符類型,我們可以自己實現類似的功能


免責聲明!

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



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