1. 程式人生 > >Python描述符深入理解

Python描述符深入理解

Python的描述符乍眼看去簡單,但是細節方面如果不注意容易掉坑,總結以下幾個坑,以作備忘,先看程式碼:

class D:
    def __get__(self, inst, owner):
        if inst is None:
            return self
        else:
            print('I am in the D.__get__')
            return inst.__dict__['d']    # 返回例項的d屬性
    def __set__(self, inst, value):
        if not isinstance(value, str):
            raise AttributeError('Value must be str')
        print('I am in the D.__set__')
        inst.__dict__['d'] = value # 設定例項自身d屬性,只能使用__dict__形式,否則又會呼叫__get__,從而陷入無限迴圈

class C:
    d = D()
    def __init__(self, value):
        self.d = value # 在初始化的時候設定d屬性,注意此時呼叫__get__函式,並不是設定例項本身的屬性

>>> c = C('shy')
I am in the D.__set__
>>> c.__dict__
{'d': 'shy'}
>>> c.d
I am in the D.__get__
'shy'

總結:

  1. 描述符只能做類屬性,不能作為例項屬性,當一個屬性是描述符時,例項查詢這個屬性會直接在類裡面查詢而忽略例項自身的空間,如上,例項自身有同名的d屬性,但是當通過c.d呼叫的時候,呼叫的是描述符d。
  2. 在描述符裡獲取或者設定例項的同名屬性時,需要用inst.__dict__形式訪問,上面如果寫成inst.d,則會陷入無限迴圈。

再來看一段程式碼:

class D:
    def __get__(self, inst, owner):
        if inst is None:
            return self
        else:
            print('I am in D.__get__')
            return self.value
    def __set__(self, inst, value):
        print('I am in D.__set__')
        self.value = value

class C:
      d = D()

>>> c1 = C()
>>> c2 = C()
>>> c1.d = 2
I am in D.__set__
>>> c2.d = 3
I am in D.__set__
>>> c1.d
I am in D.__get__
3
>>> c2.d
I am in D.__get__
3

class D:
    def __get__(self, inst, owner):
        if inst is None:
            return self
        else:
            print('I am in D.__get__')
            return inst.value
    def __set__(self, inst, value):
        print('I am in D.__set__')
        inst.value = value

class C:
    d = D()

>>> c1 = C()
>>> c2 = C()
>>> c1.d = 2
I am in D.__set__
>>> c2.d = 3
I am in D.__set__
>>> c1.d
I am in D.__get__
2
>>> c2.d
I am in D.__get__
3

總結:
屬性可以儲存在描述符內部,也可以儲存在例項,但是如果儲存為描述符內部,則為所有例項共享,所以一般把描述符的狀態的資訊儲存在描述符內部,而把例項相關的資訊儲存在例項側。