描述器由一個類對象定義,實現(xiàn)了__get__
方法,__set__
, __delete__
方法的類對象叫做描述器類對象,我們指的描述器是指這個類的實例對象。
描述器對象能夠?qū)崿F(xiàn)了兩個類的交互作用,將其中的一個類操作自己屬性的行為轉(zhuǎn)而映射到另一個類的一個方法上,實現(xiàn)更多靈活的操作。
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類直接調(diào)用x 方法
B.x # 本應(yīng)該返回A(),但是由于A()是一個描述器對象,將會自動調(diào)用A()的__get__方法獲取返回值
# 使用B類實例調(diào)用
b = B()
b.x # 由于A是數(shù)據(jù)描述器,實例化中的x 和 類中的 x 屬性同名,self.x = 123, 將會觸發(fā)轉(zhuǎn)而調(diào)用A類中的`__set__`方法,instance為B類的self實例,value為值123
A 類是一個描述器類,當(dāng)A的實例作為其他對象的的一個屬性時,其他對象對該屬性進(jìn)行操作時,將會調(diào)用這個描述類中對應(yīng)操作的__get__
, __set__
, __delete__
方法。
非數(shù)據(jù)描述器是指只實現(xiàn)了 __get__
方法的描述器,類屬性名將不會影響實例屬性的賦值操作,當(dāng)實例屬性和類屬性同名時候,實例依然優(yōu)先訪問自己的屬性值.
class A: # 這是一個描述器類
def __get__(self, instance, owner):
pass
class B:
x = A() # x 和 y 是兩個不同的實例描述器實例對象
y = A()
def __init__(self):
self.x = 123 # A是數(shù)據(jù)描述器,該語句正常執(zhí)行
b = B()
b.x # 123優(yōu)先訪問自己的x屬性, 返回值 123
b.y # 由于b沒有y屬性,調(diào)用B的類屬性y,便調(diào)用了A類中__get__函數(shù)獲取返回值
# 同時參數(shù)instance為b,owner為b的類,即B
B.x # 調(diào)用A中的__get__,由于是B類調(diào)用x,instance參數(shù)為None,owner為B
數(shù)據(jù)描述器在實現(xiàn)了__set__
方法的基礎(chǔ)上,還實現(xiàn)了__set__
或__delete__
方法其中的至少一個。當(dāng)類屬性關(guān)聯(lián)一個屬性描述器時,通過實例訪問與描述器同名的屬性時候,仍然會觸發(fā)數(shù)據(jù)描述器,轉(zhuǎn)而調(diào)用描述器中的__get__
, __set__
,__delete__
方法,實例對象自己的屬性將不能直接訪問。
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() 初始化一個B實例,調(diào)用__init__初始化方法,執(zhí)行self.x = 123, 由于B類的 x屬性是一個數(shù)據(jù)描述器,實例對 x 屬性的訪問仍然會被描述器攔截,self.x = 123 將會轉(zhuǎn)而調(diào)用A類中的__set__方法(因為這是賦值操作調(diào)用__set__,訪問操作調(diào)用__get__),將會打印__set__方法中的"set"
print(b.x) 通過b.x 訪問x屬性時同樣被描述器攔截,對應(yīng)調(diào)用描述器__get__方法,打印__get__中的"get",并返回None值,故print(b.x)打印None
Note:在使用反射函數(shù)setattr(b, "x", 123)
時,效果如同b.x = 123
,將會調(diào)用描述器,所以在描述器中不要出現(xiàn)instance.x = 123
類似的使用實例訪問 x 屬性的操作,否則將再次出發(fā)描述器,進(jìn)而產(chǎn)生遞歸。 在描述器想實現(xiàn)對實例屬性的增加或者訪問,應(yīng)該操作該實例屬性字典來避免遞歸現(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)用了描述器,但是描述器中的操作和普通賦值取值操作一致,在外部使用時感覺不到描述器的存在。這樣我們就可以這些屬性進(jìn)行賦值時對參數(shù)進(jìn)行一些限制了。例如實現(xiàn)一個參數(shù)的類型檢測功能。
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):
# 在賦值前對參數(shù)進(jìn)行檢測,滿足條件才添加到字典中
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ù)注解才會進(jìn)行檢測
class B:
x = A("x", int)
def __init__(self, x:int):
self.x = x # 賦值調(diào)用描述器
上面編碼是實現(xiàn)了對 x
參數(shù)的類型檢查,在描述器中__set__
方法中實現(xiàn)了參數(shù)的類型檢查,這樣即使在以后對實例的x
屬性進(jìn)行重新賦值,仍然會再次檢查新賦值的類型。
上面代碼采用硬編碼對x屬性進(jìn)行檢查,可以使用一個裝飾器對B類動態(tài)添加需要檢測的屬性。
class TypCheck:
def __init__(self, name, typ):
self.name = name
self.typ = typ
def __get__(self, instance, owner):
if instance is None: # 通過類名調(diào)用描述器屬性時 instance為None值
raise TypeError("類名無法調(diào)用該方法,只支持實例調(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類動態(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
# 簡單測試
a = A("name", 10)
print(a.name)
print(a.age)
# 當(dāng)參數(shù)類型不匹配時
a.name = 12 # TypeError
a.age = "12" # TypeError
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
網(wǎng)站標(biāo)題:Python描述器-創(chuàng)新互聯(lián)
分享鏈接:http://jinyejixie.com/article30/dhosso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、建站公司、外貿(mào)網(wǎng)站建設(shè)、虛擬主機、移動網(wǎng)站建設(shè)、企業(yè)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容