python 描述器

描述器定義

① 實(shí)現(xiàn)描述符協(xié)議
  • 實(shí)現(xiàn) __get__(), __set__(), __delete__() 方法
② 實(shí)例&類(lèi)屬性、方法的訪問(wèn)
  • 1、類(lèi)和實(shí)例的訪問(wèn)
    # 標(biāo)準(zhǔn)的描述符定義
    class Quanty:
        def __init__(self, weight):
            self.weight = weight
        
        def __get__(self, instance, cls):
            return self.weight
            
        def __set__(self, instance, value):
            self.weight = value
    
    class Person:
        name = 'PY_ONE'
        weight = Quanty(90)  # 描述器實(shí)例
        
        def __init__(self, age):
            self.age = age
        
        @classmethod
        def get_verson(cls):
            return 1.0
        
        @staticmethod
        def find_version():
            return 1.0
    
    • 使用 vars()__dict__查看對(duì)象的屬性
    # Person 類(lèi)對(duì)象
    >>> vars(Person)
    >>> mappingproxy({
        '__dict__': <attribute '__dict__' of 'Person' objects>,
        '__doc__': None,
        '__init__': <function __main__.Person.__init__>,
        '__module__': '__main__',
        '__weakref__': <attribute '__weakref__' of 'Person' objects>,
        'find_version': <staticmethod at 0x10544ed30>,
        'get_verson': <classmethod at 0x10544e240>,
        'name': 'PY_ONE',
        'weight': <__main__.Quanty at 0x105472cc0>
    })
    
    # Person 實(shí)例對(duì)象
    >>> per = Person(18)
    >>> vars(per)
    >>> {'age': 18}
    
    # 可以看到,實(shí)例對(duì)象 per 只有 age 一個(gè)屬性,類(lèi) Person 有 name, get_version, find_version, weight 屬性
    
    • 類(lèi)屬性可以使用實(shí)例或類(lèi)對(duì)象訪問(wèn),只有類(lèi)才能修改
    >>> p1, p2 = Person(16), Person(17)
    >>> p1.name  # PY_ONE
    >>> p2.name  # PY_ONE
    >>> Person.name  # PY_ONE
    
    >>> p1.name = 'PY_WJ'
    >>> Person.name  # PY_ONE
    >>> p2.name  # PY_ONE
    >>> p1.name  # PY_ONE
    
    >>> Person.name = '_PY'
    >>> p2.name  # _PY
    >>> p1.name  # PY_WJ
    
    • 屬性訪問(wèn)的原理與描述器
    # 屬性訪問(wèn)就是基于 Python 的描述器實(shí)現(xiàn)。類(lèi)或者實(shí)例通過(guò) . 操作符訪問(wèn)屬性,會(huì)先訪問(wèn)對(duì)象的 __dict__,如果沒(méi)有再訪問(wèn)類(lèi)(或父類(lèi),元類(lèi)除外)的 __dict__。如果最后這個(gè) __dict__ 的對(duì)象是一個(gè)描述器,則會(huì)調(diào)用描述器的 __get__ 方法
    
    >>> p2.name  # 等同于調(diào)用 p2.__dict__,發(fā)現(xiàn) name 不在此屬性字典中,則嘗試調(diào)用 type(p2).__dict__,發(fā)現(xiàn)此屬性存在,且此屬性的對(duì)象是字符串,故為 type(p2).__dict__['name']
    
    >>> p2.weight  # 和上述調(diào)用一致,不過(guò)在 type(p2).__dict__['weight']的對(duì)象是一個(gè)描述器,故最終調(diào)用如下 type(p2).__dict__['weight'].__get__(p2, type(p2))
    
    • 類(lèi)方法的調(diào)用
    >>> p1.get_version  # 調(diào)用同上,發(fā)現(xiàn) p1 實(shí)例中沒(méi)有 get_version 屬性,且 type(p1).__dict__['get_version'] 的對(duì)象是 classmethod 實(shí)例,故最終調(diào)用如下 type(p1).__dict__['get_version'].__get__(Person)
    
    • 靜態(tài)方法同類(lèi)方法
    # 同類(lèi)方法的調(diào)用
    
  • 2、使用描述符對(duì)類(lèi)屬性做驗(yàn)證
    • 示例如下
    # 驗(yàn)證正數(shù)的描述器
    class PositiveNum:
        
        def __init__(self, key_col):
            self._key_col = key_col
        
        def __get__(self, instance, cls):
            return instance.__dict__[self._key_col]
            
        def __set__(self, instance, value):
            if value < 0:
                raise ValueError('value must be > 0')
            instance.__dict__[self._key_col] = value
    
    # 商品類(lèi)
    class Good:
        price = PositiveNum('price')
        quantity = PositiveNum('quantity')
        
        def __init__(self, price, quantity):
            self.price = price
            self.quantity = quantity
    
    >>> g = Good(12, -1)  # ValueError
    >>> g = Good(12, 8)  # {'price': 12, 'quantity': 8}
    
    # 疑問(wèn)點(diǎn) a
    >>> g.price = -1  # ValueError:  會(huì)執(zhí)行type(g).__dict__['price'].__set__(g, -1)
    
    # 疑問(wèn)點(diǎn) b
    >>> Good.price = -1  # TODO 
    
  • 3、疑問(wèn)點(diǎn)解釋
    • 疑問(wèn)點(diǎn) a
    # 只是對(duì)類(lèi)屬性聲明為描述符對(duì)象,為何對(duì)實(shí)例屬性進(jìn)行賦值操作的時(shí)候,也會(huì)被描述符覆蓋?
    
    答: 
    1、描述符分為覆蓋型描述符和非覆蓋型描述符,而判斷是否為覆蓋型描述符的條件就是該描述符是否實(shí)現(xiàn)了 __set__(self, obj, value) 方法。
    2、對(duì)于同名實(shí)例屬性,如上文的 price 和 quantity 屬性,如果描述符為覆蓋型描述符,則描述符同樣會(huì)覆蓋對(duì)實(shí)例屬性的復(fù)制操作。
    3、當(dāng)描述符和實(shí)例字典中的某個(gè)屬性重名,按訪問(wèn)優(yōu)先級(jí)為 覆蓋型描述符 > 同名實(shí)例字典中屬性 > 非覆蓋型描述符
    
    • 疑問(wèn)點(diǎn) b
    # 類(lèi)屬性已聲明為描述符對(duì)象,為何對(duì)類(lèi)屬性賦值會(huì)覆蓋描述符?
    
    答:
    1、不管描述符是不是覆蓋型描述符,對(duì)類(lèi)屬性賦值都能覆蓋描述符
    2、這是一種猴子補(bǔ)丁技術(shù)
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容