descriptors描述器:
站在用戶的角度思考問題,與客戶深入溝通,找到西夏網站設計與西夏網站推廣的解決方案,憑借多年的經驗,讓設計與互聯網技術結合,創造個性化、用戶體驗好的作品,建站類型包括:成都網站制作、成都做網站、企業官網、英文網站、手機端網站、網站推廣、域名注冊、網站空間、企業郵箱。業務覆蓋西夏地區。descriptor的表現:
用到3個魔術方法:__get__()、__set__()、__delete__();
object.__get__(self,instance,owner)
object.__set__(self,instance,value)
object.__delete__(self,instance)
self,指代當前實例,調用者;
instance,是owner的實例;
owner,是屬性所屬的類;
py中,一個類實現了__get__()、__set__()、__delete__()三個方法中的任何一個方法,就是描述器;
如果僅實現了__get__(),就是non-data descriptor非數據描述器;
如果同時實現了__get__()、__set__(),就是data descriptor數據描述器,如@property;
如果一個類的類屬性設置為描述器,那么這個類它被稱為owner屬主,如B類中類屬性x = A();
關鍵記住:類屬性;
注:
當一個類的類屬性,是另一個類的實例時,這“另一個類”上有__get__()、__set__()、__delete__()三者之一,它就是個描述器的類,在類屬性上訪問另一個類的實例時,它就會觸發__get__()方法;如果是通過實例的屬性訪問另一個類的實例self.x = A(),它不會觸發__get__()方法;
non-data descriptor和data descriptor:
理解1:
如果一個類的屬性是一個數據描述器,對實例屬性的操作(該實例屬性與類屬性名相同時)相當于操作類屬性;
理解2:
官方是用優先級定義的;
一個類的類屬性是一個數據描述器,對該類的實例屬性的操作,該類的實例的__dict__優先級降低(數據描述器的優先級高于實例的__dict__);
如果是非數據描述器,則實例的__dict__高于描述器的優先級;
屬性查找順序:
實例的__dict__優先于non-data descriptor;
data descriptor優先于實例的__dict__;
__delete__有同樣的效果,有此方法就是data descriptor;
B.x = 500?? #對描述器不能這么用,賦值即定義,直接把類屬性覆蓋了,注意,不要直接用類來操作,盡管是在類上定義,也要用實例來操作,除非明確知道在干什么
print(B.x)
b = B()
b.x = 600?? #雖觸發了__set__(),未把類屬性覆蓋,也寫不進__dict__中,被__set__()攔截了,對數據起到一定保護作用
本質:
查看實例的__dict__可知,data descriptor,實例的__dict__都被__set__()攔住,實例的屬性名與類屬性名相同時,寫不進實例的__dict__中;
原來不是什么data descriptor優先級高,而是把實例的屬性從__dict__中給去掉了(實例的屬性名與類的屬性名相同),造成了該屬性如果是data descriptor優先訪問的假象,說到底,屬性訪問的順序從來就沒變過;
py的描述器應用非常廣泛;
py的所有方法(包括@staticmethod、@classmethod、__init__()),都是non-data descriptor,因此,實例可以重新定義和覆蓋方法,這允許單個實例獲取與同一類的其它實例不同的行為;
@property類實現是一個data descriptor,因此,實例不能覆蓋屬性的行為;
例:
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就是一個描述器,對類B的屬性x讀取,成為對類A的實例的訪問就會調用__get__()
print('A.__get__',self,instance,owner)
# return self?? #解決B.x.a1報錯NoneType問題,黑魔法,通過屬性描述器來操作屬主,拿到屬主的類,可動態的改所有屬性
class B:
x = A()?? #當一個類的類屬性,是另一個類的實例時,這“另一個類”上有__get__()、__set__()、__delete__()三者之一,它就是個描述器的類,在類屬性上訪問另一個類的實例時,它就會觸發__get__()方法
def __init__(self):
print('B.__init__')
# self.x = 100
self.x = A()?? #如果是通過實例的屬性訪問另一個類的實例self.x = A(),它不會觸發__get__()方法
print(B.x)??#V,要在類屬性上訪問,才觸發__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)?? #實例屬性上訪問不會觸發__get__()
輸出:
A.__init__
A.__get__ <__main__.A object at 0x7f3d53b2bb38> None <class '__main__.B'> ??#依次為A的實例,None沒有類B的實例,類B
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的實例b的__dict__為空,只能向上訪問類屬性的
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()?? #觸發__set__()
print("*"*20)
print(b.x)?? #數據描述器,對實例屬性的操作(該實例屬性與類屬性的名字相同)相當于操作類屬性,查看實例的__dict__(為空)可知(向上找了類屬性)
print("*"*20)
print(b.x.a1)
print("#"*20)
print(b.__dict__)
print(B.__dict__)
# B.x = 500?? #對描述器不能這么用,賦值即定義,直接把類屬性覆蓋了,注意,不要直接用類來操作,盡管是在類上定義,也要用實例來操作,除非明確知道在干什么
# print(B.x)
輸出:
A.__init__
A.__get__ <__main__.A object at 0x7f63acbcd400> None <class '__main__.B'>
<__main__.A object at 0x7f63acbcd400>
********************
A.__get__ <__main__.A object at 0x7f63acbcd400> None <class '__main__.B'>
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> <class '__main__.B'>
<__main__.A object at 0x7f63acbcd400>
********************
A.__get__ <__main__.A object at 0x7f63acbcd400> <__main__.B object at 0x7f63acbcdb70> <class '__main__.B'>
a1test
####################
{}
{'__module__': '__main__', 'x': <__main__.A object at 0x7f63acbcd400>, '__init__': <function B.__init__ at 0x7f63acbca510>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}
例:
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?? #雖觸發了__set__(),未把類屬性覆蓋,也寫不進實例的__dict__中(查看實例的__dict__可知),被__set__()攔截了,對數據起到一定保護作用
print("*"*20)
print(b.x)?? #調用__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> <class '__main__.B'>
<__main__.A object at 0x7f05dd15eb70>
********************
{}
1、實現StaticMethod裝飾器,完成staticmethod裝飾器的功能;
2、實現ClassMethod裝飾器,完成classmethod裝飾器的功能;
3、對實例的數據進行校驗;
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():?? #類裝飾器裝飾完后,原函數消失了,foo=StaticMethod(foo),成為裝飾器類的實例了,在類屬性上訪問另一個類的實例時就會觸發__get__()方法
print('test function')
@staticmethod
def foo2():
print('test2 func')
f = A.foo
print(f)
f()
A.foo2()
A().foo()
A().foo2()
輸出
<function A.foo at 0x7fd0675bb488>
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)?? #固定下來,返回一個新函數
class A:
@ClassMethod
def bar(cls):
print(cls.__name__)
# print(A.bar)
# print()
A.bar()
print()
A().bar()
print()
print(A.__dict__)
輸出:
__init__ <function A.bar at 0x7f2998f97e18>
__get__ <__main__.ClassMethod object at 0x7f2999039d68> None <class '__main__.A'>
A
__get__ <__main__.ClassMethod object at 0x7f2999039d68> <__main__.A object at 0x7f2999039da0> <class '__main__.A'>
A
{'__module__': '__main__', 'bar': <__main__.ClassMethod object at 0x7f2999039d68>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
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)?? #硬編碼,需改進
age = Typed(int)
def __init__(self,name:str,age:int):
self.name = name
self.age = age
p1 = Person('tom',18)
3、
改進:用裝飾器+描述器,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))?? #動態類屬性注入
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)?? #動態類屬性注入
# 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 <class 'str'>
age <class 'int'>
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
習題:
1、將鏈表,封裝成容器:
要求:
1)提供__getitem__()、__iter__()、__setitem__();
2)使用一個列表,輔助完成上面的方法;
3)進階:不使用列表,完成上面的方法;
2、實現類property裝飾器,類名稱為Property;
另外有需要云服務器可以了解下創新互聯cdcxhl.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業上云的綜合解決方案,具有“安全穩定、簡單易用、服務可用性高、性價比高”等特點與優勢,專為企業上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
網站題目:33面向對象8_descriptors-創新互聯
地址分享:http://vcdvsql.cn/article28/ggpcp.html
成都網站建設公司_創新互聯,為您提供小程序開發、關鍵詞優化、自適應網站、網站制作、網站導航、外貿網站建設
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯