python中描述符的學(xué)習(xí)

什么是描述符

描述符是Python新式類的關(guān)鍵點(diǎn)之一墓贿,它為對(duì)象屬性提供強(qiáng)大的API,你可以認(rèn)為描述符是表示對(duì)象屬性的一個(gè)代理炼列。當(dāng)需要屬性時(shí),可根據(jù)你遇到的情況音比,通過(guò)描述符進(jìn)行訪問(wèn)他(摘自Python核心編程)俭尖。

實(shí)例解析

  • 使用類方法創(chuàng)建描述符

就是將某種特殊類型的類的實(shí)例指派給另一個(gè)類的屬性(注意:這里是類屬性,而不是對(duì)象屬性)。而這種特殊類型的類就是實(shí)現(xiàn)了__get__稽犁,__set__,__delete__的新式類(即繼承object)焰望。

__get__(self, object, type)                  # 用于得到一個(gè)屬性的值
__set__(self, obj, val)                      # 用于為一個(gè)屬性賦值
__delete__(self, obj)                        # 刪除某個(gè)屬性時(shí)被調(diào)用,但很少用到

其中只實(shí)現(xiàn)了__set__()方法的被當(dāng)做方法描述符已亥,或者是非數(shù)據(jù)描述符熊赖。那些同時(shí)實(shí)現(xiàn)了__set__()__get__()方法的類被稱作數(shù)據(jù)描述符。

首先定義一個(gè)數(shù)據(jù)描述符類

# coding=utf-8
class Descriptor(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        print "訪問(wèn)屬性"
        return self.value

    def __set__(self, instance, value):
        print "設(shè)置屬性值"
        self.value = value

再來(lái)定義一個(gè)調(diào)用數(shù)據(jù)描述符的類

class Myclass(object):
    desc = Descriptor(5)

if __name__ == '__main__':
    print Myclass.desc

訪問(wèn)結(jié)果為:

訪問(wèn)屬性
5

發(fā)現(xiàn)訪問(wèn)Myclass的desc屬性時(shí)虑椎,調(diào)用了描述符的__get__()方法震鹉。這就達(dá)到了描述符的作用(可以改變對(duì)象屬性的訪問(wèn))。

調(diào)用原理:對(duì)于類屬性描述符捆姜,如果解析器發(fā)現(xiàn)屬性x是一個(gè)描述符的話传趾,在內(nèi)部通過(guò)type.__getattribute__()(訪問(wèn)屬性時(shí)無(wú)條件調(diào)用,最先調(diào)用)泥技,它能把Class.x轉(zhuǎn)換成Class.__dict__[‘x’].__get__(None, Class)來(lái)訪問(wèn)

上面把描述符定義成了類屬性墨缘,那我們要把他定義成對(duì)象屬性會(huì)有什么樣的異同呢?

# coding=utf-8
class Descriptor(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        print "訪問(wèn)屬性"
        return self.value

    def __set__(self, instance, value):
        print "設(shè)置屬性值"
        self.value = value


class Myclass(object):
    def __init__(self):
        self.desc = Descriptor(5)

if __name__ == '__main__':
    myclass = Myclass()
    print myclass.desc

輸出結(jié)果為:

<__main__.Descriptor object at 0x0000000002DFAC18>

并沒(méi)有像我們預(yù)期的那樣調(diào)用__get__()方法零抬,只是說(shuō)他是Descriptor的一個(gè)對(duì)象镊讼。

這是因?yàn)楫?dāng)訪問(wèn)實(shí)例描述符對(duì)象時(shí),obj.__getattribute__()會(huì)將myclass.desc轉(zhuǎn)換為type(myclass).__dict__['desc'].__get__(myclass, type(myclass))平夜,即到類屬性中去尋找desc蝶棋,并調(diào)用他的__get__()方法。而Myclass類中沒(méi)有desc屬性忽妒,所以無(wú)法訪調(diào)用到__get__方法.
描述符是一個(gè)類屬性玩裙,必須定義在類的層次上, 而不能單純的定義為對(duì)象屬性。

那么當(dāng)定義類屬性描述符對(duì)象和實(shí)例屬性名字相同時(shí)段直,會(huì)有什么樣的效果呢吃溅?

# coding=utf-8
class Descriptor(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        print "訪問(wèn)屬性"
        return self.value

    def __set__(self, instance, value):
        print "設(shè)置屬性值"
        self.value = value


class Myclass(object):
    desc = Descriptor(5)

    def __init__(self, desc):
        self.desc = desc     # 與類屬性同名的屬性

if __name__ == '__main__':
    myclass = Myclass(3)
    print myclass.desc

運(yùn)行結(jié)果如下:

設(shè)置屬性值
訪問(wèn)屬性
3

可以看出初始化時(shí)訪問(wèn)了描述符的__set__()方法,訪問(wèn)屬性值時(shí)訪問(wèn)了描述符的__get__()方法鸯檬。這樣為什么又調(diào)用描述符的方法了呢决侈?
為了解釋這個(gè)問(wèn)題,我們要先說(shuō)一下在python中訪問(wèn)一個(gè)屬性的優(yōu)先級(jí)喧务,如下:

  • 類屬性
  • 數(shù)據(jù)描述符
  • 實(shí)例屬性
  • 非數(shù)據(jù)描述符
  • 默認(rèn)為getattr()(找不到的情況下)

然后我們打印出上面代碼類和實(shí)例的屬性列表:

if __name__ == '__main__':
    myclass = Myclass(3)
    print "instance:  ", myclass.__dict__
    print "Class:    ", Myclass.__dict__

結(jié)果如下:

instance:   {}
Class:     {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Myclass' objects>, '__weakref__': <attribute '__weakref__' of 'Myclass' objects>, '__doc__': None, '__init__': <function __init__ at 0x00000000030F9438>, 'desc': <__main__.Descriptor object at 0x00000000030E8B70>}

可以發(fā)現(xiàn)實(shí)例對(duì)象的屬性中并沒(méi)有desc赖歌,而相反,類屬性中卻有它功茴。這是為什么呢庐冯?

按照上面的屬性訪問(wèn)優(yōu)先級(jí)的理論,數(shù)據(jù)描述符 > 實(shí)例屬性坎穿。當(dāng)python發(fā)現(xiàn)實(shí)例對(duì)象的字典中有與定義的描述符有相同名字的對(duì)象時(shí)展父,描述符優(yōu)先返劲,會(huì)覆蓋掉實(shí)例屬性。python會(huì)改寫(xiě)默認(rèn)的行為栖茉,去調(diào)用描述符的方法來(lái)代替篮绿。

我們來(lái)驗(yàn)證一下上面的理論,優(yōu)先級(jí)實(shí)例屬性 > 非數(shù)據(jù)描述符衡载。首先我們定義一下非數(shù)據(jù)描述符(只有__get__()方法)

# coding=utf-8
class Descriptor(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        print "訪問(wèn)屬性"
        return self.value

    # def __set__(self, instance, value):
    #     print "設(shè)置屬性值"
    #     self.value = value


class Myclass(object):
    desc = Descriptor(5)

    def __init__(self, desc):
        self.desc = desc     # 與類屬性同名的屬性

if __name__ == '__main__':
    myclass = Myclass(3)
    print myclass.desc
    print "instance:  ", myclass.__dict__
    print "Class:    ", Myclass.__dict__

運(yùn)行一下:

3
instance:   {'desc': 3}
Class:     {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Myclass' objects>, '__weakref__': <attribute '__weakref__' of 'Myclass' objects>, '__doc__': None, '__init__': <function __init__ at 0x0000000002E393C8>, 'desc': <__main__.Descriptor object at 0x0000000002E28B00>}

可以看出搔耕,這種情況下訪問(wèn)實(shí)例屬性,并沒(méi)有調(diào)用描述符的__get__()方法痰娱。而是調(diào)用了本身的屬性弃榨。可以看出理論是正確的梨睁。

  • 使用屬性類型創(chuàng)建描述符

屬性是一種有用的特殊類型的描述符鲸睛。他們是用來(lái)處理所有對(duì)實(shí)例屬性的訪問(wèn),其工作方法和前面說(shuō)過(guò)的描述符類似坡贺。通過(guò)使用 property()官辈,可以輕松地為任意屬性創(chuàng)建可用的描述符。

property內(nèi)建函數(shù)有四個(gè)參數(shù):property(fget=None, fset=None, fdel=None, doc=None)遍坟。

fget:屬性獲取方法
fset:屬性設(shè)置方法
fdel:屬性刪除方法
doc:文檔描述

來(lái)看一下實(shí)現(xiàn):

class PropertyDesc(object):
    def __init__(self):
        self._name = ''

    def fget(self):
        print "Getting: %s" % self._name
        return self._name
    
    def fset(self, value):
        print "Setting: %s" % value
        self._name = value

    def fdel(self):
        print "Deleting: %s" %self._name
        del self._name
    name = property(fget, fset, fdel, "I'm the property.")
if __name__ == '__main__':
    pro = PropertyDesc()
    pro.name = "haha"
    print pro.name
    del pro.name

結(jié)果如下:

Setting: haha
Getting: haha
haha
Deleting: haha

這樣實(shí)現(xiàn)描述符拳亿,雖然簡(jiǎn)單。但屬性多的話就造成代碼臃腫不堪愿伴。

  • 使用屬性修飾符創(chuàng)建描述符
class PropertyDesc(object):
    def __init__(self):
        self._name = ''

    @property
    def name(self):
        print "Getting: %s" % self._name
        return self._name

    @name.setter
    def name(self, value):
        print "Setting: %s" % value
        self._name = value

    @name.deleter
    def name(self):
        print "Deleting: %s" %self._name
        del self._name

if __name__ == '__main__':
    pro = PropertyDesc()
    pro.name = "haha"
    print pro.name
    del pro.name

運(yùn)行結(jié)果如下:

Setting: haha
Getting: haha
haha
Deleting: haha

看肺魁,代碼運(yùn)行如初。具體原理就不再贅述隔节《炀可以打印出PropertyDesc類和實(shí)例pro的屬性列表進(jìn)行思考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怎诫,一起剝皮案震驚了整個(gè)濱河市瘾晃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌幻妓,老刑警劉巖蹦误,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異涌哲,居然都是意外死亡胖缤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)阀圾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人狗唉,你說(shuō)我怎么就攤上這事初烘。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵肾筐,是天一觀的道長(zhǎng)哆料。 經(jīng)常有香客問(wèn)我,道長(zhǎng)吗铐,這世上最難降的妖魔是什么东亦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮唬渗,結(jié)果婚禮上典阵,老公的妹妹穿的比我還像新娘。我一直安慰自己镊逝,他們只是感情好壮啊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著撑蒜,像睡著了一般歹啼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上座菠,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天狸眼,我揣著相機(jī)與錄音,去河邊找鬼浴滴。 笑死拓萌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巡莹。 我是一名探鬼主播司志,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼降宅!你這毒婦竟也來(lái)了骂远?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤腰根,失蹤者是張志新(化名)和其女友劉穎激才,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體额嘿,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘸恼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了册养。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片东帅。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖球拦,靈堂內(nèi)的尸體忽然破棺而出靠闭,到底是詐尸還是另有隱情帐我,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布愧膀,位于F島的核電站拦键,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏檩淋。R本人自食惡果不足惜芬为,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蟀悦。 院中可真熱鬧媚朦,春花似錦、人聲如沸熬芜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涎拉。三九已至瑞侮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鼓拧,已是汗流浹背半火。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留季俩,地道東北人钮糖。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像酌住,于是被迫代替她去往敵國(guó)和親店归。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 10,934評(píng)論 6 13
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理酪我,服務(wù)發(fā)現(xiàn)消痛,斷路器,智...
    卡卡羅2017閱讀 134,639評(píng)論 18 139
  • 要點(diǎn): 函數(shù)式編程:注意不是“函數(shù)編程”都哭,多了一個(gè)“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍钪壬 傩浴?..
    victorsungo閱讀 1,480評(píng)論 0 6
  • 之前寫(xiě)了關(guān)于買(mǎi)房,保險(xiǎn)的文章欺矫。不過(guò)纱新,我是程序猿呀,還是寫(xiě)一點(diǎn)工作相關(guān)的文章吧穆趴。不要那么“不務(wù)正業(yè)”脸爱,哈哈。在麻瓜編...
    9abda844c1aa閱讀 246評(píng)論 0 0
  • 不是520的表白日未妹,也不是5月份的母親節(jié)阅羹,而是“大學(xué)生心理健康節(jié)”勺疼。 2000年教寂,由北京師范大學(xué)心...
    psychology凡心閱讀 926評(píng)論 0 8