python 描述器

描述器定義

① 實現(xiàn)描述符協(xié)議
  • 實現(xiàn) __get__(), __set__(), __delete__() 方法
② 實例&類屬性婉刀、方法的訪問
  • 1、類和實例的訪問
    # 標準的描述符定義
    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)  # 描述器實例
        
        def __init__(self, age):
            self.age = age
        
        @classmethod
        def get_verson(cls):
            return 1.0
        
        @staticmethod
        def find_version():
            return 1.0
    
    • 使用 vars()__dict__查看對象的屬性
    # Person 類對象
    >>> 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 實例對象
    >>> per = Person(18)
    >>> vars(per)
    >>> {'age': 18}
    
    # 可以看到序仙,實例對象 per 只有 age 一個屬性,類 Person 有 name, get_version, find_version, weight 屬性
    
    • 類屬性可以使用實例或類對象訪問鲁豪,只有類才能修改
    >>> 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
    
    • 屬性訪問的原理與描述器
    # 屬性訪問就是基于 Python 的描述器實現(xiàn)潘悼。類或者實例通過 . 操作符訪問屬性,會先訪問對象的 __dict__爬橡,如果沒有再訪問類(或父類治唤,元類除外)的 __dict__。如果最后這個 __dict__ 的對象是一個描述器糙申,則會調(diào)用描述器的 __get__ 方法
    
    >>> p2.name  # 等同于調(diào)用 p2.__dict__宾添,發(fā)現(xiàn) name 不在此屬性字典中,則嘗試調(diào)用 type(p2).__dict__柜裸,發(fā)現(xiàn)此屬性存在缕陕,且此屬性的對象是字符串,故為 type(p2).__dict__['name']
    
    >>> p2.weight  # 和上述調(diào)用一致疙挺,不過在 type(p2).__dict__['weight']的對象是一個描述器扛邑,故最終調(diào)用如下 type(p2).__dict__['weight'].__get__(p2, type(p2))
    
    • 類方法的調(diào)用
    >>> p1.get_version  # 調(diào)用同上,發(fā)現(xiàn) p1 實例中沒有 get_version 屬性铐然,且 type(p1).__dict__['get_version'] 的對象是 classmethod 實例蔬崩,故最終調(diào)用如下 type(p1).__dict__['get_version'].__get__(Person)
    
    • 靜態(tài)方法同類方法
    # 同類方法的調(diào)用
    
  • 2恶座、使用描述符對類屬性做驗證
    • 示例如下
    # 驗證正數(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
    
    # 商品類
    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}
    
    # 疑問點 a
    >>> g.price = -1  # ValueError:  會執(zhí)行type(g).__dict__['price'].__set__(g, -1)
    
    # 疑問點 b
    >>> Good.price = -1  # TODO 
    
  • 3、疑問點解釋
    • 疑問點 a
    # 只是對類屬性聲明為描述符對象沥阳,為何對實例屬性進行賦值操作的時候跨琳,也會被描述符覆蓋?
    
    答: 
    1桐罕、描述符分為覆蓋型描述符和非覆蓋型描述符脉让,而判斷是否為覆蓋型描述符的條件就是該描述符是否實現(xiàn)了 __set__(self, obj, value) 方法。
    2冈绊、對于同名實例屬性侠鳄,如上文的 price 和 quantity 屬性,如果描述符為覆蓋型描述符死宣,則描述符同樣會覆蓋對實例屬性的復制操作伟恶。
    3、當描述符和實例字典中的某個屬性重名毅该,按訪問優(yōu)先級為 覆蓋型描述符 > 同名實例字典中屬性 > 非覆蓋型描述符
    
    • 疑問點 b
    # 類屬性已聲明為描述符對象博秫,為何對類屬性賦值會覆蓋描述符?
    
    答:
    1眶掌、不管描述符是不是覆蓋型描述符挡育,對類屬性賦值都能覆蓋描述符
    2、這是一種猴子補丁技術(shù)
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朴爬,一起剝皮案震驚了整個濱河市即寒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌召噩,老刑警劉巖母赵,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異具滴,居然都是意外死亡凹嘲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門构韵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曹抬,“玉大人吨瞎,你說我怎么就攤上這事。” “怎么了哼绑?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵矮固,是天一觀的道長稼稿。 經(jīng)常有香客問我册招,道長,這世上最難降的妖魔是什么萎攒? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任遇八,我火速辦了婚禮矛绘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刃永。我一直安慰自己货矮,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布斯够。 她就那樣靜靜地躺著囚玫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪读规。 梳的紋絲不亂的頭發(fā)上抓督,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音束亏,去河邊找鬼铃在。 笑死,一個胖子當著我的面吹牛碍遍,可吹牛的內(nèi)容都是我干的定铜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼怕敬,長吁一口氣:“原來是場噩夢啊……” “哼揣炕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起东跪,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤畸陡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后虽填,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罩锐,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年卤唉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仁期。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡桑驱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跛蛋,到底是詐尸還是另有隱情熬的,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布赊级,位于F島的核電站押框,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏理逊。R本人自食惡果不足惜橡伞,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一盒揉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兑徘,春花似錦刚盈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至崭闲,卻和暖如春肋联,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刁俭。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工橄仍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薄翅。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓沙兰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翘魄。 傳聞我的和親對象是個殘疾皇子鼎天,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容