一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

python理解描述符(descriptor)

 wenxuefeng360 2022-07-16 發(fā)布于四川

Descriptor基礎(chǔ)

python中的描述符可以用來(lái)定義觸發(fā)自動(dòng)執(zhí)行的代碼,它像是一個(gè)對(duì)象屬性操作(訪(fǎng)問(wèn)、賦值、刪除)的代理類(lèi)一樣。前面介紹過(guò)的property是描述符的一種。

大致流程是這樣的:

  1. 定義一個(gè)描述符類(lèi)D,其內(nèi)包含一個(gè)或多個(gè)__get__()__set__()、__delete__()方法
  2. 將描述符類(lèi)D的實(shí)例對(duì)象d賦值給另一個(gè)要代理的類(lèi)中某個(gè)屬性attr,即attr = D()
  3. 之后訪(fǎng)問(wèn)、賦值、刪除attr屬性,將會(huì)自動(dòng)觸發(fā)描述符類(lèi)中的__get__()、__set__()、__delete__()方法

簡(jiǎn)言之,就是創(chuàng)建一個(gè)描述符類(lèi),它的實(shí)例對(duì)象作為另一個(gè)類(lèi)的屬性。

要定義描述符類(lèi)很簡(jiǎn)單,只要某個(gè)類(lèi)中包含了下面一個(gè)或多個(gè)方法,就算是滿(mǎn)足描述符協(xié)議,就是描述符類(lèi),就可以作為屬性操作的代理器。

class Descriptor():
    def __get__(self, instance, owner):...
    def __set__(self, instance, value):...
    def __delete__(self, instance):...

需要注意的是,__get__的返回值需要是屬性值或拋異常,另外兩個(gè)方法要返回None。

還需注意的是不要把__delete____del__搞混了,前者是實(shí)現(xiàn)描述符協(xié)議的一個(gè)方法,后者是對(duì)象銷(xiāo)毀函數(shù)(也常稱(chēng)為析構(gòu)函數(shù))。

先不管這幾個(gè)方法中的參數(shù),看一個(gè)示例先:

class Descriptor():
    def __get__(self, instance, owner):
        print("self: %s\ninstance: %s\nowner: %s" % (self, instance, owner))

class S:
    # 描述符的示例對(duì)象作為S的屬性
    attr = Descriptor()

s1 = S()
s1.attr  # 訪(fǎng)問(wèn)對(duì)象屬性

print("-" * 30)
S.attr   # 訪(fǎng)問(wèn)類(lèi)屬性

輸出結(jié)果:

self: <__main__.Descriptor object at 0x030C02D0>
instance: <__main__.S object at 0x030C0AB0>
owner: <class '__main__.S'>
------------------------------
self: <__main__.Descriptor object at 0x030C02D0>
instance: None
owner: <class '__main__.S'>

不難看出,在訪(fǎng)問(wèn)類(lèi)S中的屬性attr時(shí),表示訪(fǎng)問(wèn)描述符類(lèi)的實(shí)例對(duì)象,它會(huì)自動(dòng)調(diào)用描述符類(lèi)中的__get__方法。

在這個(gè)方法中,3個(gè)參數(shù)self、instance、owner分別對(duì)應(yīng)的內(nèi)容從結(jié)果中已經(jīng)顯示出來(lái)了。它們之間有以下等價(jià)關(guān)系:

s1.attr -> Descriptor.__get__(S.attr, s1, S)
S.attr  -> Descriptor.__get__(S.attr, None, S)

所以,這里解釋下__get__(self, instance, owner)中的三個(gè)參數(shù):

  • self:描述符對(duì)象自身,也就是被代理類(lèi)S中的屬性attr
  • instance:被代理類(lèi)的實(shí)例對(duì)象。所以訪(fǎng)問(wèn)類(lèi)屬性(class.attr)時(shí)為None
  • owner:將描述符對(duì)象附加到哪個(gè)類(lèi)上,其實(shí)是instance所屬的類(lèi),也就是type(instance)

再解釋下這里相關(guān)的幾個(gè)角色:

  • Descriptor:是描述符類(lèi),也是代理者
  • S:是另一個(gè)類(lèi),是托管類(lèi)、客戶(hù)類(lèi),也就是參數(shù)中的owner
  • attr = Descriptor():是描述符的實(shí)例對(duì)象,attr是托管類(lèi)的屬性,也就參數(shù)中的self
  • s1:是托管類(lèi)實(shí)例對(duì)象,也就是參數(shù)中的instance

按照descriptor的功能,大概可以用上面的方式去定義各個(gè)角色。當(dāng)然,角色的定義沒(méi)什么限制。

descriptor的作用發(fā)揮在哪

當(dāng)定義了一個(gè)類(lèi)后,可以訪(fǎng)問(wèn)、賦值、刪除它的屬性,這些操作也同樣適用于它的實(shí)例對(duì)象。

例如Foo類(lèi):

class Foo():
    ...

f = Foo()
a = f.bar   # 訪(fǎng)問(wèn)屬性
f.bar = b   # 賦值屬性
del f.bar   # 刪除屬性

decriptor發(fā)揮作用的時(shí)候就在于執(zhí)行這3類(lèi)操作的時(shí)候:

  • 當(dāng)訪(fǎng)問(wèn)x.d的時(shí)候,將自動(dòng)調(diào)用描述符類(lèi)中的__get__
  • 當(dāng)賦值x.d的時(shí)候,將自動(dòng)調(diào)用描述符類(lèi)中的__set__
  • 當(dāng)刪除x.d的時(shí)候,將自動(dòng)調(diào)用描述符類(lèi)中的__delete__

考慮一下:如果x所屬的類(lèi)中已經(jīng)定義了__getattr__、__setattr__、__delattr__會(huì)如何,是描述符類(lèi)中的先生效,還是x自身所屬類(lèi)的這幾個(gè)方法會(huì)生效。再繼續(xù)考慮,如果x所屬類(lèi)沒(méi)有定義,但它的父類(lèi)定義了這幾個(gè)方法,誰(shuí)會(huì)生效??勺孕袦y(cè)試或者參考我的下一篇文章。

示例1:原始代碼

假設(shè)現(xiàn)在有一個(gè)Student類(lèi),需要記錄stuid、name、score1、score2、score3信息。

class Student():
    def __init__(self, stuid, name, score1, score2, score3):
        self.stuid = stuid
        self.name = name
        self.score1 = score1
        self.score2 = score2
        self.score3 = score3

    def returnMe(self):
        return "%s, %s, %i, %i, %i" % (
            self.stuid,
            self.name,
            self.score1,
            self.score2,
            self.score3)

stu = Student("20101120", "malong", 67, 77, 88)
print(stu.returnMe())

但是現(xiàn)在有個(gè)需求,要求score1-score3的數(shù)值范圍只能是0-100分。

于是修改__init__()

class Student():
    def __init__(self, stuid, name, score1, score2, score3):
        self.stuid = stuid
        self.name = name

        if 0 <= score1 <= 100:
            self.score1 = score1
        else:
            raise ValueError("score not in [0,100]")

        if 0 <= score2 <= 100:
            self.score2 = score2
        else:
            raise ValueError("score not in [0,100]")

        if 0 <= score3 <= 100:
            self.score3 = score3
        else:
            raise ValueError("score not in [0,100]")

這個(gè)修改對(duì)于初始化Student對(duì)象時(shí)有效,但Python中屬性的賦值太過(guò)自由,之后可以隨意賦值:

stu = Student("20101120", "malong", 67, 77, 88)

stu.score1 = -23
print(stu.returnMe())

使用property

使用Property或者自定義的getter、setter或運(yùn)算符__getattr__、__setattr__重載都能解決上面的問(wèn)題,保證無(wú)法賦值超出0到100范圍內(nèi)的數(shù)值。

class Student():
    def __init__(self, stuid, name, score1, score2, score3):
        self.stuid = stuid
        self.name = name
        self._score1 = score1
        self._score2 = score2
        self._score3 = score3

    def get_score1(self):
        return self._score1

    def set_score1(self, score):
        if 0 <= score <= 100:
            self._score1 = score
        else:
            raise ValueError("score not in [0,100]")

    def get_score2(self):
        return self._score2

    def set_score2(self, score):
        if 0 <= score <= 100:
            self._score2 = score
        else:
            raise ValueError("score not in [0,100]")

    def get_score3(self):
        return self._score3

    def set_score3(self, score):
        if 0 <= score <= 100:
            self._score3 = score
        else:
            raise ValueError("score not in [0,100]")

    score1 = property(get_score1, set_score1)
    score2 = property(get_score2, set_score2)
    score3 = property(get_score3, set_score3)

    def returnMe(self):
        return "%s, %s, %i, %i, %i" % (
            self.stuid,
            self.name,
            self.score1,
            self.score2,
            self.score3)
折疊

下面測(cè)試時(shí)將拋出異常。

stu = Student("20101120", "malong", 67, 77, 88)
print(stu.returnMe())
stu.score1 = -23

但很顯然,上面的重復(fù)代碼太多了。

使用descriptor

如果使用descriptor,將很容易解決上面的問(wèn)題。只需將score1、score2、score3交給描述符類(lèi)托管即可。

from weakref import WeakKeyDictionary

class Score():
    """ score should in [0,100] """

    def __init__(self):
        self.score = WeakKeyDictionary()
        #self.score = {}

    def __get__(self, instance, owner):
        return self.score[instance]

    def __set__(self, instance, value):
        if 0 <= value <= 100:
            self.score[instance] = value
        else:
            raise ValueError("score not in [0,100]")


class Student():
    # 托管屬性定義在類(lèi)級(jí)別上
    score1 = Score()
    score2 = Score()
    score3 = Score()

    def __init__(self, stuid, name, score1, score2, score3):
        self.stuid = stuid
        self.name = name
        self.score1 = score1
        self.score2 = score2
        self.score3 = score3

    def returnMe(self):
        return "%s, %s, %i, %i, %i" % (
            self.stuid,
            self.name,
            self.score1,
            self.score2,
            self.score3)


stu = Student("20101120", "malong", 67, 77, 88)
print(stu.returnMe())
stu.score1 = -23
折疊

很明顯地,它們的代碼被完整地復(fù)用了。這里score1、score2、score3被描述符類(lèi)Score托管了,這3個(gè)分值分別被放進(jìn)了Score實(shí)例對(duì)象的dict中(是單獨(dú)存放它們還是使用dict數(shù)據(jù)結(jié)構(gòu)來(lái)保存,取決于你)。

另外,上面使用了弱引用的字典,因?yàn)槊總€(gè)屬性只在描述符對(duì)象中才會(huì)被用上,為了保證Student對(duì)象被銷(xiāo)毀的時(shí)候能釋放這些資源,所以采用弱引用,避免出現(xiàn)內(nèi)存泄漏。

參考資料

 

轉(zhuǎn)載請(qǐng)注明出處:https://www.cnblogs.com/f-ck-need-u/p/10198020.html

如果覺(jué)得文章不錯(cuò),不妨給個(gè)打賞,寫(xiě)作不易,各位的支持,能激發(fā)和鼓勵(lì)我更大的寫(xiě)作熱情。謝謝!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    神马午夜福利一区二区| 欧美黑人在线一区二区| 不卡免费成人日韩精品| 内用黄老外示儒术出处| 精品al亚洲麻豆一区| 亚洲精品高清国产一线久久| 日本在线视频播放91| 日本少妇三级三级三级| 午夜激情视频一区二区| 在线观看那种视频你懂的| 久久婷婷综合色拍亚洲| 日韩精品成区中文字幕| 国产成人午夜av一区二区| 国产欧美精品对白性色| 日韩欧美中文字幕人妻| 国产乱久久亚洲国产精品| 亚洲中文字幕三区四区| 人妻露脸一区二区三区| 91后入中出内射在线| 亚洲欧美日本国产不卡| 亚洲午夜av一区二区| 久久热麻豆国产精品视频| 久久精品色妇熟妇丰满人妻91| 精品女同一区二区三区| 五月天丁香婷婷狠狠爱| 91福利视频日本免费看看| 色婷婷人妻av毛片一区二区三区| 青青操成人免费在线视频| 国产av精品一区二区| 精品熟女少妇av免费久久野外| 亚洲黑人精品一区二区欧美| 女生更色还是男生更色| 黄色三级日本在线观看| 视频在线免费观看你懂的 | 99久久精品午夜一区| 国产日韩欧美综合视频| 午夜福利92在线观看| 日韩国产精品激情一区| 日韩不卡一区二区在线| 国产中文另类天堂二区| 亚洲欧洲在线一区二区三区 |