描述符:
描述符本質(zhì)是一個(gè)新式類圾旨,在這個(gè)新式類中,至少出現(xiàn)了__get__()
、__set__()
或者__delete__()
方法中的一個(gè)廓鞠,也被稱為描述符協(xié)議帚稠。
-
__get__()
:obj.name
調(diào)用一個(gè)屬性的時(shí)候觸發(fā) -
__set__()
:obj.name = "bill"
為一個(gè)屬性賦值的時(shí)候觸發(fā) -
__delete__()
:del obj.name
采用del
刪除屬性的時(shí)候,觸發(fā)
描述符分為兩種:
- 數(shù)據(jù)描述符 至少實(shí)現(xiàn)了
__get__()
和__set__()
- 非數(shù)據(jù)描述符 沒有實(shí)現(xiàn)
__set__()
注意:對(duì)象本身調(diào)用不會(huì)觸發(fā)描述符協(xié)議床佳,當(dāng)該新式類為別的類的屬性滋早,通過別的類來調(diào)用該新式類時(shí)候才會(huì)觸發(fā)“
class Foo:
def __set__(self, instance, value):
# 可以通過__dict__屬性字典設(shè)置,在設(shè)置前可以增加判斷等操作
print("hahaha")
class Tmp:
#Foo()是數(shù)據(jù)描述符 代理了foo對(duì)象的屬性調(diào)用相關(guān)方法
foo = Foo()
result = Tmp()
result.foo = "asdf"
函數(shù)裝飾器復(fù)習(xí)
前有函數(shù)裝飾器:
def foo(func):
print("裝飾器")
return func;
@foo # 這一步就等于func1 = foo(func1)
def func1():
print("func1")
func1()
類裝飾器使用
終極代碼:類裝飾器 + 描述符
class TypedC:
def __init__(self, key, classType):
self.key = key
self.classType = classType
def __get__(self, instance, owner):
return instance.__dict__[self.key]
def __set__(self, instance, value):
if not isinstance(value, self.classType):
raise TypeError("傳入的數(shù)據(jù)類型有問題")
instance.__dict__[self.key] = value
def out_deco(**kwargs):
def in_deco(obj):
for key, value in kwargs.items():
setattr(obj, key, TypedC(key,value))
return obj
return deco
@out_deco(name=str, age=int)
class New:
def __init__(self, name, age):
self.name = name
self.age = age
print(New("alex",100).__dict__)
解釋:
@out_deco(name=str, age=int)
先忽略@砌们,即執(zhí)行右半邊方法: out_deco(name=str, age=int) -> out_deco({字典})
得到:
@in_deco(New) 相當(dāng)于 New = in_deco(New)
然后執(zhí)行in_deco(New)內(nèi)部循環(huán):
setattr(obj, key, TypedC(key,value)) 相當(dāng)于 New.key = TypedC("name", str)
自定義裝飾器
使用類裝飾器 模擬系統(tǒng)@property
的實(shí)現(xiàn)
class Deco_class:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func(instance)
class Foo:
def __init__(self):
pass
@Deco_class
def showAnim(self):
print("show anim 運(yùn)行")
obj = Foo()
obj.showAnim
改進(jìn)版:儲(chǔ)存函數(shù)執(zhí)行結(jié)果杆麸,下次不用重復(fù)計(jì)算執(zhí)行
class Deco_class:
def __init__(self, func):
self.func = func
# 因?yàn)闆]有重寫__set__方法,所以是非數(shù)據(jù)描述符浪感,優(yōu)先級(jí)比實(shí)例屬性低
def __get__(self, instance, owner):
res = self.func(instance)
# 儲(chǔ)存結(jié)果到實(shí)例對(duì)象中后 下次調(diào)用 由于優(yōu)先級(jí)的問題 會(huì)優(yōu)先從實(shí)例對(duì)象中查找showAnim方法昔头,而不是調(diào)用非數(shù)據(jù)描述符
setattr(instance, self.func.__name__, res)
return res
class Foo:
def __init__(self):
pass
@Deco_class
def showAnim(self):
print("show anim 運(yùn)行")
obj = Foo()
obj.showAnim
補(bǔ)充:
使用@property
裝飾器修飾的方法,不可以賦值篮撑,需要:
class Foo:
@property
def showAnim(self):
print("show anim 運(yùn)行")
@showAnim.setter
def showAnim(self, name):
print("show anim 運(yùn)行", name)
obj = Foo()
obj.showAnim
obj.showAnim = "bill"
另一種寫法:
class Foo:
def get_a(self):
print("get method")
def set_a(self, val):
print("set method")
def del_a(self):
print("del method")
aaa = property(get_a, set_a, del_a)
obj = Foo()
obj.aaa = "bill"