您好,登錄后才能下訂單哦!
?
descriptors描述器:
?
descriptor的表現(xiàn):
用到3個魔術方法:__get__()、__set__()、__delete__();
object.__get__(self,instance,owner)
object.__set__(self,instance,value)
object.__delete__(self,instance)
self,指代當前實例,調用者;
instance,是owner的實例;
owner,是屬性所屬的類;
?
py中,一個類實現(xiàn)了__get__()、__set__()、__delete__()三個方法中的任何一個方法,就是描述器;
如果僅實現(xiàn)了__get__(),就是non-data descriptor非數(shù)據(jù)描述器;
如果同時實現(xiàn)了__get__()、__set__(),就是data descriptor數(shù)據(jù)描述器,如@property;
如果一個類的類屬性設置為描述器,那么這個類它被稱為owner屬主,如B類中類屬性x = A();
關鍵記?。侯悓傩?;
?
注:
當一個類的類屬性,是另一個類的實例時,這“另一個類”上有__get__()、__set__()、__delete__()三者之一,它就是個描述器的類,在類屬性上訪問另一個類的實例時,它就會觸發(fā)__get__()方法;如果是通過實例的屬性訪問另一個類的實例self.x = A(),它不會觸發(fā)__get__()方法;
?
non-data descriptor和data descriptor:
理解1:
如果一個類的屬性是一個數(shù)據(jù)描述器,對實例屬性的操作(該實例屬性與類屬性名相同時)相當于操作類屬性;
理解2:
官方是用優(yōu)先級定義的;
一個類的類屬性是一個數(shù)據(jù)描述器,對該類的實例屬性的操作,該類的實例的__dict__優(yōu)先級降低(數(shù)據(jù)描述器的優(yōu)先級高于實例的__dict__);
如果是非數(shù)據(jù)描述器,則實例的__dict__高于描述器的優(yōu)先級;
?
屬性查找順序:
實例的__dict__優(yōu)先于non-data descriptor;
data descriptor優(yōu)先于實例的__dict__;
__delete__有同樣的效果,有此方法就是data descriptor;
?
B.x = 500?? #對描述器不能這么用,賦值即定義,直接把類屬性覆蓋了,注意,不要直接用類來操作,盡管是在類上定義,也要用實例來操作,除非明確知道在干什么
print(B.x)
?
b = B()
b.x = 600?? #雖觸發(fā)了__set__(),未把類屬性覆蓋,也寫不進__dict__中,被__set__()攔截了,對數(shù)據(jù)起到一定保護作用
?
本質:
查看實例的__dict__可知,data descriptor,實例的__dict__都被__set__()攔住,實例的屬性名與類屬性名相同時,寫不進實例的__dict__中;
原來不是什么data descriptor優(yōu)先級高,而是把實例的屬性從__dict__中給去掉了(實例的屬性名與類的屬性名相同),造成了該屬性如果是data descriptor優(yōu)先訪問的假象,說到底,屬性訪問的順序從來就沒變過;
?
py的描述器應用非常廣泛;
py的所有方法(包括@staticmethod、@classmethod、__init__()),都是non-data descriptor,因此,實例可以重新定義和覆蓋方法,這允許單個實例獲取與同一類的其它實例不同的行為;
@property類實現(xiàn)是一個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問題,黑魔法,通過屬性描述器來操作屬主,拿到屬主的類,可動態(tài)的改所有屬性
class B:
??? x = A()?? #當一個類的類屬性,是另一個類的實例時,這“另一個類”上有__get__()、__set__()、__delete__()三者之一,它就是個描述器的類,在類屬性上訪問另一個類的實例時,它就會觸發(fā)__get__()方法
??? def __init__(self):
??????? print('B.__init__')
??????? # self.x = 100
??????? self.x = A()?? #如果是通過實例的屬性訪問另一個類的實例self.x = A(),它不會觸發(fā)__get__()方法
?
print(B.x) ??#V,要在類屬性上訪問,才觸發(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)?? #實例屬性上訪問不會觸發(fā)__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()?? #觸發(fā)__set__()
print("*"*20)
print(b.x)?? #數(shù)據(jù)描述器,對實例屬性的操作(該實例屬性與類屬性的名字相同)相當于操作類屬性,查看實例的__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?? #雖觸發(fā)了__set__(),未把類屬性覆蓋,也寫不進實例的__dict__中(查看實例的__dict__可知),被__set__()攔截了,對數(shù)據(jù)起到一定保護作用
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、實現(xiàn)StaticMethod裝飾器,完成staticmethod裝飾器的功能;
2、實現(xiàn)ClassMethod裝飾器,完成classmethod裝飾器的功能;
3、對實例的數(shù)據(jù)進行校驗;
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),成為裝飾器類的實例了,在類屬性上訪問另一個類的實例時就會觸發(fā)__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)?? #固定下來,返回一個新函數(shù)
?
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))?? #動態(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)?? #動態(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 <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、實現(xiàn)類property裝飾器,類名稱為Property;
?
?
?
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。