descriptors描述器:
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到西夏網(wǎng)站設(shè)計(jì)與西夏網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都網(wǎng)站制作、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋西夏地區(qū)。descriptor的表現(xiàn):
用到3個(gè)魔術(shù)方法:__get__()、__set__()、__delete__();
object.__get__(self,instance,owner)
object.__set__(self,instance,value)
object.__delete__(self,instance)
self,指代當(dāng)前實(shí)例,調(diào)用者;
instance,是owner的實(shí)例;
owner,是屬性所屬的類;
py中,一個(gè)類實(shí)現(xiàn)了__get__()、__set__()、__delete__()三個(gè)方法中的任何一個(gè)方法,就是描述器;
如果僅實(shí)現(xiàn)了__get__(),就是non-data descriptor非數(shù)據(jù)描述器;
如果同時(shí)實(shí)現(xiàn)了__get__()、__set__(),就是data descriptor數(shù)據(jù)描述器,如@property;
如果一個(gè)類的類屬性設(shè)置為描述器,那么這個(gè)類它被稱為owner屬主,如B類中類屬性x = A();
關(guān)鍵記?。侯悓傩?;
注:
當(dāng)一個(gè)類的類屬性,是另一個(gè)類的實(shí)例時(shí),這“另一個(gè)類”上有__get__()、__set__()、__delete__()三者之一,它就是個(gè)描述器的類,在類屬性上訪問(wèn)另一個(gè)類的實(shí)例時(shí),它就會(huì)觸發(fā)__get__()方法;如果是通過(guò)實(shí)例的屬性訪問(wèn)另一個(gè)類的實(shí)例self.x = A(),它不會(huì)觸發(fā)__get__()方法;
non-data descriptor和data descriptor:
理解1:
如果一個(gè)類的屬性是一個(gè)數(shù)據(jù)描述器,對(duì)實(shí)例屬性的操作(該實(shí)例屬性與類屬性名相同時(shí))相當(dāng)于操作類屬性;
理解2:
官方是用優(yōu)先級(jí)定義的;
一個(gè)類的類屬性是一個(gè)數(shù)據(jù)描述器,對(duì)該類的實(shí)例屬性的操作,該類的實(shí)例的__dict__優(yōu)先級(jí)降低(數(shù)據(jù)描述器的優(yōu)先級(jí)高于實(shí)例的__dict__);
如果是非數(shù)據(jù)描述器,則實(shí)例的__dict__高于描述器的優(yōu)先級(jí);
屬性查找順序:
實(shí)例的__dict__優(yōu)先于non-data descriptor;
data descriptor優(yōu)先于實(shí)例的__dict__;
__delete__有同樣的效果,有此方法就是data descriptor;
B.x = 500?? #對(duì)描述器不能這么用,賦值即定義,直接把類屬性覆蓋了,注意,不要直接用類來(lái)操作,盡管是在類上定義,也要用實(shí)例來(lái)操作,除非明確知道在干什么
print(B.x)
b = B()
b.x = 600?? #雖觸發(fā)了__set__(),未把類屬性覆蓋,也寫(xiě)不進(jìn)__dict__中,被__set__()攔截了,對(duì)數(shù)據(jù)起到一定保護(hù)作用
本質(zhì):
查看實(shí)例的__dict__可知,data descriptor,實(shí)例的__dict__都被__set__()攔住,實(shí)例的屬性名與類屬性名相同時(shí),寫(xiě)不進(jìn)實(shí)例的__dict__中;
原來(lái)不是什么data descriptor優(yōu)先級(jí)高,而是把實(shí)例的屬性從__dict__中給去掉了(實(shí)例的屬性名與類的屬性名相同),造成了該屬性如果是data descriptor優(yōu)先訪問(wèn)的假象,說(shuō)到底,屬性訪問(wèn)的順序從來(lái)就沒(méi)變過(guò);
py的描述器應(yīng)用非常廣泛;
py的所有方法(包括@staticmethod、@classmethod、__init__()),都是non-data descriptor,因此,實(shí)例可以重新定義和覆蓋方法,這允許單個(gè)實(shí)例獲取與同一類的其它實(shí)例不同的行為;
@property類實(shí)現(xiàn)是一個(gè)data descriptor,因此,實(shí)例不能覆蓋屬性的行為;
例:
class A:
def __init__(self):
print('A.__init__')
self.a1 = 'a1'
class B:
x = A()
def __init__(self):
print('B.__init__')
self.x = 100
print(B.x.a1)
b = B()
# print(b.x.a1)?? # X,AttributeError: 'int' object has no attribute 'a1'
輸出:
A.__init__
a1
B.__init__
例:
class A:
def __init__(self):
print('A.__init__')
self.a1 = 'a1'
def __get__(self, instance, owner): ??#類A中定義了__get__(),類A就是一個(gè)描述器,對(duì)類B的屬性x讀取,成為對(duì)類A的實(shí)例的訪問(wèn)就會(huì)調(diào)用__get__()
print('A.__get__',self,instance,owner)
# return self?? #解決B.x.a1報(bào)錯(cuò)NoneType問(wèn)題,黑魔法,通過(guò)屬性描述器來(lái)操作屬主,拿到屬主的類,可動(dòng)態(tài)的改所有屬性
class B:
x = A()?? #當(dāng)一個(gè)類的類屬性,是另一個(gè)類的實(shí)例時(shí),這“另一個(gè)類”上有__get__()、__set__()、__delete__()三者之一,它就是個(gè)描述器的類,在類屬性上訪問(wèn)另一個(gè)類的實(shí)例時(shí),它就會(huì)觸發(fā)__get__()方法
def __init__(self):
print('B.__init__')
# self.x = 100
self.x = A()?? #如果是通過(guò)實(shí)例的屬性訪問(wèn)另一個(gè)類的實(shí)例self.x = A(),它不會(huì)觸發(fā)__get__()方法
print(B.x)??#V,要在類屬性上訪問(wèn),才觸發(fā)__get__(),該例__get__()方法返回None
# print(B.x.a1)?? #X,AttributeError: 'NoneType' object has no attribute 'a1',解決辦法:在類A的__get__()添加返回值return self
b = B()
print(b.x)
print(b.x.a1)?? #實(shí)例屬性上訪問(wèn)不會(huì)觸發(fā)__get__()
輸出:
A.__init__
A.__get__ <__main__.A object at 0x7f3d53b2bb38> None
None
B.__init__
A.__init__
<__main__.A object at 0x7f3d53b2bba8>
a1
例:
class A:
def __init__(self):
print('A.__init__')
self.a1 = 'a1test'
def __get__(self, instance, owner):
print('A.__get__',self,instance,owner)
return self
def __set__(self, instance, value):?? #有__set__()后,類B的實(shí)例b的__dict__為空,只能向上訪問(wèn)類屬性的
print('A.__set__',self,instance,value)
class B:
x = A()
def __init__(self):
print('B.__init__')
# self.x = 100
self.x = A()
print(B.x)
print("*"*20)
print(B.x.a1)
print("#"*20)
b = B()?? #觸發(fā)__set__()
print("*"*20)
print(b.x)?? #數(shù)據(jù)描述器,對(duì)實(shí)例屬性的操作(該實(shí)例屬性與類屬性的名字相同)相當(dāng)于操作類屬性,查看實(shí)例的__dict__(為空)可知(向上找了類屬性)
print("*"*20)
print(b.x.a1)
print("#"*20)
print(b.__dict__)
print(B.__dict__)
# B.x = 500?? #對(duì)描述器不能這么用,賦值即定義,直接把類屬性覆蓋了,注意,不要直接用類來(lái)操作,盡管是在類上定義,也要用實(shí)例來(lái)操作,除非明確知道在干什么
# print(B.x)
輸出:
A.__init__
A.__get__ <__main__.A object at 0x7f63acbcd400> None
<__main__.A object at 0x7f63acbcd400>
********************
A.__get__ <__main__.A object at 0x7f63acbcd400> None
a1test
####################
B.__init__
A.__init__
A.__set__ <__main__.A object at 0x7f63acbcd400> <__main__.B object at 0x7f63acbcdb70> <__main__.A object at 0x7f63acbcdba8>
********************
A.__get__ <__main__.A object at 0x7f63acbcd400> <__main__.B object at 0x7f63acbcdb70>
<__main__.A object at 0x7f63acbcd400>
********************
A.__get__ <__main__.A object at 0x7f63acbcd400> <__main__.B object at 0x7f63acbcdb70>
a1test
####################
{}
{'__module__': '__main__', 'x': <__main__.A object at 0x7f63acbcd400>, '__init__':
例:
class A:
def __init__(self):
print('A.__init__')
self.a1 = 'a1test'
def __get__(self, instance, owner):
print('A.__get__',self,instance,owner)
return self
def __set__(self, instance, value):
print('A.__set__',self,instance,value)
class B:
x = A()
def __init__(self):
print('B.__init__')
# self.x = 100
self.x = A()
b = B()
print("*"*20)
b.x = 600?? #雖觸發(fā)了__set__(),未把類屬性覆蓋,也寫(xiě)不進(jìn)實(shí)例的__dict__中(查看實(shí)例的__dict__可知),被__set__()攔截了,對(duì)數(shù)據(jù)起到一定保護(hù)作用
print("*"*20)
print(b.x)?? #調(diào)用__get__()
print("*"*20)
print(b.__dict__)
輸出:
A.__init__
B.__init__
A.__init__
A.__set__ <__main__.A object at 0x7f05dd15eb70> <__main__.B object at 0x7f05dd15eba8> <__main__.A object at 0x7f05dd15ebe0>
********************
A.__set__ <__main__.A object at 0x7f05dd15eb70> <__main__.B object at 0x7f05dd15eba8> 600
********************
A.__get__ <__main__.A object at 0x7f05dd15eb70> <__main__.B object at 0x7f05dd15eba8>
<__main__.A object at 0x7f05dd15eb70>
********************
{}
1、實(shí)現(xiàn)StaticMethod裝飾器,完成staticmethod裝飾器的功能;
2、實(shí)現(xiàn)ClassMethod裝飾器,完成classmethod裝飾器的功能;
3、對(duì)實(shí)例的數(shù)據(jù)進(jìn)行校驗(yàn);
class Person:
def __init__(self,name:str,age:int):
self.name = name
self.age = age
1、
class StaticMethod:
def __init__(self,fn):
# print('__init__',fn)
self.fn = fn
def __get__(self, instance, owner):
# print('__get__',self,instance,owner)
return self.fn
class A:
@StaticMethod
def foo():?? #類裝飾器裝飾完后,原函數(shù)消失了,foo=StaticMethod(foo),成為裝飾器類的實(shí)例了,在類屬性上訪問(wèn)另一個(gè)類的實(shí)例時(shí)就會(huì)觸發(fā)__get__()方法
print('test function')
@staticmethod
def foo2():
print('test2 func')
f = A.foo
print(f)
f()
A.foo2()
A().foo()
A().foo2()
輸出
test function
test2 func
test function
test2 func
2、
from functools import partial
class ClassMethod:
def __init__(self,fn):
???print('__init__',fn)
self.fn = fn
def __get__(self, instance, cls):
print('__get__', self, instance, cls)
# return self.fn(cls)?? #X,NoneType
?return partial(self.fn, cls)?? #固定下來(lái),返回一個(gè)新函數(shù)
class A:
@ClassMethod
def bar(cls):
print(cls.__name__)
# print(A.bar)
# print()
A.bar()
print()
A().bar()
print()
print(A.__dict__)
輸出:
__init__
__get__ <__main__.ClassMethod object at 0x7f2999039d68> None
A
__get__ <__main__.ClassMethod object at 0x7f2999039d68> <__main__.A object at 0x7f2999039da0>
A
{'__module__': '__main__', 'bar': <__main__.ClassMethod object at 0x7f2999039d68>, '__dict__':
3、
class Typed:
def __init__(self,type):
self.type = type
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
print('T.__set__',self,instance,value)
if not isinstance(value,self.type):
raise ValueError('value')
class Person:
name = Typed(str)?? #硬編碼,需改進(jìn)
age = Typed(int)
def __init__(self,name:str,age:int):
self.name = name
self.age = age
p1 = Person('tom',18)
3、
改進(jìn):用裝飾器+描述器,py中大量使用;
import inspect
class Typed:
def __init__(self, type):
self.type = type
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
print('set', self, instance, value)
if not isinstance(value, self.type):
raise ValueError(value)
class TypeAssert:
def __init__(self, cls):
self.cls = cls
params = inspect.signature(self.cls).parameters
# print(params)
for name, param in params.items():
print(name, param.annotation)
if param.annotation != param.empty:
setattr(self.cls, name, Typed(param.annotation))?? #動(dòng)態(tài)類屬性注入
def __call__(self, name, age):
# params = inspect.signature(self.cls).parameters
# print(params)
# for name,param in params.items():
#???? print(name,param.annotation)
#???? if param.annotation != param.empty:
#???????? setattr(self.cls,name,Typed(param.annotation))
p = self.cls(name, age)
return p
@TypeAssert
class Person:
# name = Typed(str)?? #動(dòng)態(tài)類屬性注入
# age = Typed(age)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p1 = Person('jerry', 18)
p2 = Person('tom', 16)
print(id(p1))
print(id(p2))
輸出:
name
age
set <__main__.Typed object at 0x7f596a121da0> <__main__.Person object at 0x7f596a121dd8> jerry
set <__main__.Typed object at 0x7f596a121cf8> <__main__.Person object at 0x7f596a121dd8> 18
set <__main__.Typed object at 0x7f596a121da0> <__main__.Person object at 0x7f596a0af940> tom
set <__main__.Typed object at 0x7f596a121cf8> <__main__.Person object at 0x7f596a0af940> 16
140022008389080
140022007920960
習(xí)題:
1、將鏈表,封裝成容器:
要求:
1)提供__getitem__()、__iter__()、__setitem__();
2)使用一個(gè)列表,輔助完成上面的方法;
3)進(jìn)階:不使用列表,完成上面的方法;
2、實(shí)現(xiàn)類property裝飾器,類名稱為Property;
另外有需要云服務(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)景需求。