Python描述器-創(chuàng)新互聯(lián)

描述器由一個(gè)類對(duì)象定義,實(shí)現(xiàn)了__get__方法,__set__, __delete__方法的類對(duì)象叫做描述器類對(duì)象,我們指的描述器是指這個(gè)類的實(shí)例對(duì)象。

在水富等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作按需求定制網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都全網(wǎng)營(yíng)銷,成都外貿(mào)網(wǎng)站制作,水富網(wǎng)站建設(shè)費(fèi)用合理。

描述器對(duì)象能夠?qū)崿F(xiàn)了兩個(gè)類的交互作用,將其中的一個(gè)類操作自己屬性的行為轉(zhuǎn)而映射到另一個(gè)類的一個(gè)方法上,實(shí)現(xiàn)更多靈活的操作。

class A:   # 這是一個(gè)描述器類
    def __get__(self, instance, owner):
        pass

    def __set__(self, instance, value):
        pass

    def __delete__(self, instance):
        pass

class B:
    x = A()

    def __init__(self):
        self.x = 123

# 使用B類直接調(diào)用x 方法 
B.x    # 本應(yīng)該返回A(),但是由于A()是一個(gè)描述器對(duì)象,將會(huì)自動(dòng)調(diào)用A()的__get__方法獲取返回值

# 使用B類實(shí)例調(diào)用
b = B()
b.x    # 由于A是數(shù)據(jù)描述器,實(shí)例化中的x 和 類中的 x 屬性同名,self.x = 123, 將會(huì)觸發(fā)轉(zhuǎn)而調(diào)用A類中的`__set__`方法,instance為B類的self實(shí)例,value為值123

A 類是一個(gè)描述器類,當(dāng)A的實(shí)例作為其他對(duì)象的的一個(gè)屬性時(shí),其他對(duì)象對(duì)該屬性進(jìn)行操作時(shí),將會(huì)調(diào)用這個(gè)描述類中對(duì)應(yīng)操作的__get__, __set__, __delete__方法。

非數(shù)據(jù)描述器和數(shù)據(jù)描述器

非數(shù)據(jù)描述器是指只實(shí)現(xiàn)了 __get__方法的描述器,類屬性名將不會(huì)影響實(shí)例屬性的賦值操作,當(dāng)實(shí)例屬性和類屬性同名時(shí)候,實(shí)例依然優(yōu)先訪問(wèn)自己的屬性值.

class A:   # 這是一個(gè)描述器類
    def __get__(self, instance, owner):
        pass

class B:
    x = A()    # x 和 y 是兩個(gè)不同的實(shí)例描述器實(shí)例對(duì)象
    y = A()
    def __init__(self):
        self.x = 123     # A是數(shù)據(jù)描述器,該語(yǔ)句正常執(zhí)行

b = B()
b.x         # 123優(yōu)先訪問(wèn)自己的x屬性, 返回值 123
b.y         # 由于b沒(méi)有y屬性,調(diào)用B的類屬性y,便調(diào)用了A類中__get__函數(shù)獲取返回值
            # 同時(shí)參數(shù)instance為b,owner為b的類,即B   
B.x         # 調(diào)用A中的__get__,由于是B類調(diào)用x,instance參數(shù)為None,owner為B

數(shù)據(jù)描述器在實(shí)現(xiàn)了__set__方法的基礎(chǔ)上,還實(shí)現(xiàn)了__set____delete__方法其中的至少一個(gè)。當(dāng)類屬性關(guān)聯(lián)一個(gè)屬性描述器時(shí),通過(guò)實(shí)例訪問(wèn)與描述器同名的屬性時(shí)候,仍然會(huì)觸發(fā)數(shù)據(jù)描述器,轉(zhuǎn)而調(diào)用描述器中的__get__, __set__,__delete__方法,實(shí)例對(duì)象自己的屬性將不能直接訪問(wèn)。

class A:   # 數(shù)據(jù)描述器類
    def __get__(self, instance, owner):
        print("get")

    def __set__(self, instance, value):
        print("set")

class B:
    x = A()
    def __init__(self):
        self.x = 123

b = B()
print(b.x)
----- 執(zhí)行結(jié)果分析 -------
b = B() 初始化一個(gè)B實(shí)例,調(diào)用__init__初始化方法,執(zhí)行self.x = 123, 由于B類的 x屬性是一個(gè)數(shù)據(jù)描述器,實(shí)例對(duì) x 屬性的訪問(wèn)仍然會(huì)被描述器攔截,self.x = 123 將會(huì)轉(zhuǎn)而調(diào)用A類中的__set__方法(因?yàn)檫@是賦值操作調(diào)用__set__,訪問(wèn)操作調(diào)用__get__),將會(huì)打印__set__方法中的"set"
print(b.x) 通過(guò)b.x 訪問(wèn)x屬性時(shí)同樣被描述器攔截,對(duì)應(yīng)調(diào)用描述器__get__方法,打印__get__中的"get",并返回None值,故print(b.x)打印None

Note:在使用反射函數(shù)setattr(b, "x", 123)時(shí),效果如同b.x = 123,將會(huì)調(diào)用描述器,所以在描述器中不要出現(xiàn)instance.x = 123類似的使用實(shí)例訪問(wèn) x 屬性的操作,否則將再次出發(fā)描述器,進(jìn)而產(chǎn)生遞歸。 在描述器想實(shí)現(xiàn)對(duì)實(shí)例屬性的增加或者訪問(wèn),應(yīng)該操作該實(shí)例屬性字典來(lái)避免遞歸現(xiàn)象

class A:   # 數(shù)據(jù)描述器類
    def __init__(self, args):
        self.args = args

    def __get__(self, instance, owner):
        return instance.__dict__(self.args)

    def __set__(self, instance, value):
        instance.__dict__(self.args) = value

class B:
    x = A("x")
    def __init__(self):
        self.x = 123

上面的程序雖然調(diào)用了描述器,但是描述器中的操作和普通賦值取值操作一致,在外部使用時(shí)感覺(jué)不到描述器的存在。這樣我們就可以這些屬性進(jìn)行賦值時(shí)對(duì)參數(shù)進(jìn)行一些限制了。例如實(shí)現(xiàn)一個(gè)參數(shù)的類型檢測(cè)功能。

import inspect

class A:   # 數(shù)據(jù)描述器類
    def __init__(self, args, typ):
        self.args = args
        self.typ = typ

    def __get__(self, instance, owner):
        return instance.__dict__(self.args)

    def __set__(self, instance, value):  
    # 在賦值前對(duì)參數(shù)進(jìn)行檢測(cè),滿足條件才添加到字典中
        if isinstance(value, self.typ):
            instance.__dict__(self.args) = value
        else:
            raise TypeError("'{}' need the type of {}".format(self.args, self.typ))

def get_type(cls):
    sig = inspect.signature(cls)
    for name, parmas_obj in sig.parameters.items():
        if params.annotation is not sig.empty:  # 定義了參數(shù)注解才會(huì)進(jìn)行檢測(cè)

class B:
    x = A("x", int)
    def __init__(self, x:int):
        self.x = x            # 賦值調(diào)用描述器

上面編碼是實(shí)現(xiàn)了對(duì) x 參數(shù)的類型檢查,在描述器中__set__方法中實(shí)現(xiàn)了參數(shù)的類型檢查,這樣即使在以后對(duì)實(shí)例的x屬性進(jìn)行重新賦值,仍然會(huì)再次檢查新賦值的類型。

上面代碼采用硬編碼對(duì)x屬性進(jìn)行檢查,可以使用一個(gè)裝飾器對(duì)B類動(dòng)態(tài)添加需要檢測(cè)的屬性。

class TypCheck:
    def __init__(self, name, typ):
        self.name = name
        self.typ = typ

    def __get__(self, instance, owner):
        if instance is None: # 通過(guò)類名調(diào)用描述器屬性時(shí) instance為None值
            raise TypeError("類名無(wú)法調(diào)用該方法,只支持實(shí)例調(diào)用")
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, self.typ):
            raise TypeError("{} {}".format(self.name, self.typ))
        instance.__dict__[self.name] = value

def inject(cls):  
# 裝飾器,為A類動(dòng)態(tài)注入類似于上例中x = A(x, int)類型檢查的描述器
    sig = inspect.signature(cls)
    for name, typ in sig.parameters.items():
        if typ.annotation is not sig.empty:
            # cls.__dict__[name] = Typ_check(name, typ.annotation)
            setattr(cls, name, TypCheck(name, typ.annotation))
    print(cls.__dict__)
    return cls

@Inject
class A:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

# 簡(jiǎn)單測(cè)試
a = A("name", 10)
print(a.name)
print(a.age)

# 當(dāng)參數(shù)類型不匹配時(shí)
a.name = 12   # TypeError
a.age = "12"  # TypeError

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

網(wǎng)站標(biāo)題:Python描述器-創(chuàng)新互聯(lián)
分享鏈接:http://www.muchs.cn/article30/dhosso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、建站公司、外貿(mào)網(wǎng)站建設(shè)虛擬主機(jī)、移動(dòng)網(wǎng)站建設(shè)企業(yè)建站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)