中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python描述器

發布時間:2020-08-03 21:14:19 來源:網絡 閱讀:343 作者:wx5b87e6d52af84 欄目:編程語言

描述器由一個類對象定義,實現了__get__方法,__set__, __delete__方法的類對象叫做描述器類對象,我們指的描述器是指這個類的實例對象。

描述器對象能夠實現了兩個類的交互作用,將其中的一個類操作自己屬性的行為轉而映射到另一個類的一個方法上,實現更多靈活的操作。

class A:   # 這是一個描述器類
    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類直接調用x 方法 
B.x    # 本應該返回A(),但是由于A()是一個描述器對象,將會自動調用A()的__get__方法獲取返回值

# 使用B類實例調用
b = B()
b.x    # 由于A是數據描述器,實例化中的x 和 類中的 x 屬性同名,self.x = 123, 將會觸發轉而調用A類中的`__set__`方法,instance為B類的self實例,value為值123 

A 類是一個描述器類,當A的實例作為其他對象的的一個屬性時,其他對象對該屬性進行操作時,將會調用這個描述類中對應操作的__get__, __set__, __delete__方法。

非數據描述器和數據描述器

非數據描述器是指只實現了 __get__方法的描述器,類屬性名將不會影響實例屬性的賦值操作,當實例屬性和類屬性同名時候,實例依然優先訪問自己的屬性值.

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

class B:
    x = A()    # x 和 y 是兩個不同的實例描述器實例對象
    y = A()
    def __init__(self):
        self.x = 123     # A是數據描述器,該語句正常執行

b = B()
b.x         # 123優先訪問自己的x屬性, 返回值 123
b.y         # 由于b沒有y屬性,調用B的類屬性y,便調用了A類中__get__函數獲取返回值
            # 同時參數instance為b,owner為b的類,即B   
B.x         # 調用A中的__get__,由于是B類調用x,instance參數為None,owner為B

數據描述器在實現了__set__方法的基礎上,還實現了__set____delete__方法其中的至少一個。當類屬性關聯一個屬性描述器時,通過實例訪問與描述器同名的屬性時候,仍然會觸發數據描述器,轉而調用描述器中的__get__, __set__,__delete__方法,實例對象自己的屬性將不能直接訪問。

class A:   # 數據描述器類
    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)
----- 執行結果分析 -------
b = B() 初始化一個B實例,調用__init__初始化方法,執行self.x = 123, 由于B類的 x屬性是一個數據描述器,實例對 x 屬性的訪問仍然會被描述器攔截,self.x = 123 將會轉而調用A類中的__set__方法(因為這是賦值操作調用__set__,訪問操作調用__get__),將會打印__set__方法中的"set"
print(b.x) 通過b.x 訪問x屬性時同樣被描述器攔截,對應調用描述器__get__方法,打印__get__中的"get",并返回None值,故print(b.x)打印None

Note:在使用反射函數setattr(b, "x", 123)時,效果如同b.x = 123,將會調用描述器,所以在描述器中不要出現instance.x = 123類似的使用實例訪問 x 屬性的操作,否則將再次出發描述器,進而產生遞歸。 在描述器想實現對實例屬性的增加或者訪問,應該操作該實例屬性字典來避免遞歸現象

class A:   # 數據描述器類
    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

上面的程序雖然調用了描述器,但是描述器中的操作和普通賦值取值操作一致,在外部使用時感覺不到描述器的存在。這樣我們就可以這些屬性進行賦值時對參數進行一些限制了。例如實現一個參數的類型檢測功能。

import inspect

class A:   # 數據描述器類
    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):  
    # 在賦值前對參數進行檢測,滿足條件才添加到字典中
        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:  # 定義了參數注解才會進行檢測

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

上面編碼是實現了對 x 參數的類型檢查,在描述器中__set__方法中實現了參數的類型檢查,這樣即使在以后對實例的x屬性進行重新賦值,仍然會再次檢查新賦值的類型。

上面代碼采用硬編碼對x屬性進行檢查,可以使用一個裝飾器對B類動態添加需要檢測的屬性。

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

    def __get__(self, instance, owner):
        if instance is None: # 通過類名調用描述器屬性時 instance為None值
            raise TypeError("類名無法調用該方法,只支持實例調用")
        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類動態注入類似于上例中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

# 簡單測試
a = A("name", 10)
print(a.name)
print(a.age)

# 當參數類型不匹配時
a.name = 12   # TypeError
a.age = "12"  # TypeError
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

尉氏县| 江华| 东明县| 福清市| 泰宁县| 互助| 恩平市| 来宾市| 蓝山县| 舒城县| 汕尾市| 金坛市| 陇西县| 邳州市| 临潭县| 漠河县| 常德市| 昌都县| 鄂托克旗| 郁南县| 巫山县| 怀安县| 琼海市| 都匀市| 黎平县| 甘德县| 澄江县| 萨嘎县| 凤山县| 衡阳县| 乐至县| 新疆| 富锦市| 山西省| 开远市| 海晏县| 习水县| 丰顺县| 太康县| 屏边| 赣州市|