描述符
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi print(f1.name)#打印下实例的属性 C:\python35\python3.exe D:/pyproject/day28/描述符.py gouguoqi
但是为什么赋值和调用的时候都没有触发__get__和__set__呢
1、也就是说在自己的类里面是触发不了的,需要在另外一个类里面用(在何地用?)
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 print(Foo()) f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi print(f1.name)#打印下实例的属性 del f1.name#删除f1的name属性 class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 C:\python35\python3.exe D:/pyproject/day28/描述符.py <__main__.Foo object at 0x0000000000A44470> gouguoqi
因为Foo这类具备三个方法__get__ __set__ __delete__ 又把Foo定义成了Bar的类属性,所以这个Foo就是一个描述符
2、知道描述符在什么位置定义了,那什么时候会触发呢
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 b1=Bar()#实例化一个b1的实例 b1.x#调用实例b1的x属性,x就是描述符Foo,所以就会触发描述符的__get__ #前提是另外的一个类产生的实例去调用描述符 C:\python35\python3.exe D:/pyproject/day28/描述符.py get方法
3、现在触发了__get__方法了,其他的2种方法如何触发呢
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 b1=Bar()#实例化一个b1的实例 b1.x#调用实例b1的x属性,x就是描述符Foo,所以就会触发描述符的__get__ #前提是另外的一个类产生的实例去调用描述符 b1.x=1#赋值 del b1.x#删除 C:\python35\python3.exe D:/pyproject/day28/描述符.py get方法 set方法 delete方法
4、看一下b1的属性字典里面有没有东西
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 b1=Bar()#实例化一个b1的实例 # b1.x#调用实例b1的x属性,x就是描述符Foo,所以就会触发描述符的__get__ #前提是另外的一个类产生的实例去调用描述符 b1.x=1#赋值,会触发__set__方法,只是打印了一下,啥也没干 print(b1.x)#调用,会触发__get__方法,没有return默认是None print(b1.__dict__)#查看b1的属性字典,是空的,因为啥也没干,就只是打印了一下 C:\python35\python3.exe D:/pyproject/day28/描述符.py set方法 get方法 None {}
5、描述符分两种
(1)数据描述符:至少实现了__get__()和__set__()
class Foo:
def __get__(self,
instance, owner):
print("get方法")
def __set__(self,
instance, value):
print("set方法")
(2)非数据描述符:没有实现__set__()
class Foo:
def __get__(self,
instance, owner):
print("get方法")
注意事项:
一、描述符本身应该定义成新式类,被代理的类也应该是新式类
二、必须把描述符定义成另外一个类的类属性,不能定义到构造函数中
三、要严格遵循该优先级,优先级由高到低分别是
1、类属性
2、数据描述符
3、实例属性
4、非数据描述符
5、找不到的属性触发__getattr__()
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 def __init__(self,n): self.x=n# self.x=n等价于b1.x=1就是在给x赋值,而x就是描述符Foo() b1=Bar(10)#实例化一个b1的实例 print(b1.__dict__)#查看b1的属性字典是空的 C:\python35\python3.exe D:/pyproject/day28/描述符.py set方法 {}
去掉描述符之后,在看下b1的属性字典
因为去掉描述符之后,就跟上一个类没关系了,所以就是给当前的这个类进行简单的赋值操作了,肯定会到自己的实例的属性字典里面去
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法") def __delete__(self, instance): print("delete方法") f1=Foo()#实例化一个实例f1 f1.name="gouguoqi"#给实例增加一个属性name赋值为gouguoqi class Bar: # x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 def __init__(self,n): self.x=n# self.x=n等价于b1.x=1就是在给x赋值,而x就是b1的属性 b1=Bar(10)#实例化一个b1的实例 print(b1.__dict__) C:\python35\python3.exe D:/pyproject/day28/描述符.py {'x': 10}
6、只要Bar中的实例有赋值、调用、删除操作,都会触发描述符Foo中对应的set get delete等方法
上面当我们操作b1的x的属性的时候不是会触发描述符Foo的set方法吗,但是属性字典是空的,那是因为set方法什么事情都没干,没有真正意义上的set,我们来改下
先查看下instance和value是什么东西
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法",instance,value)#查看下instance和value是什么 #instance就是Bar的实例b1 def __delete__(self, instance): print("delete方法") class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 def __init__(self,n): self.x=n# self.x=n等价于b1.x=1就是在给x赋值,而x就是描述符Foo()就会触发Foo的set b1=Bar(10)#实例化一个b1的实例 C:\python35\python3.exe D:/pyproject/day28/描述符.py set方法 <__main__.Bar object at 0x0000000000D345C0> 10
然后真正意义的实现一下set方法的作用
这样只要触发到了Foo的__set__方法就会把属性放入到Bar的实例b1的属性字典里面
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法",instance,value)#查看下instance和value是什么 instance.__dict__["x"]=value#等价于b1.__dict__["x"]=10 def __delete__(self, instance): print("delete方法") class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 def __init__(self,n): self.x=n# self.x=n等价于b1.x=1就是在给x赋值,而x就是描述符Foo()就会触发Foo的set b1=Bar(10)#实例化一个b1的实例 print(b1.__dict__) C:\python35\python3.exe D:/pyproject/day28/描述符.py set方法 <__main__.Bar object at 0x0000000000D84550> 10 {'x': 10}
我们赋值2次试试看,结果是第二次赋值的会覆盖第一次的值
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法",instance,value)#查看下instance和value是什么 instance.__dict__["x"]=value#等价于b1.__dict__["x"]=10 def __delete__(self, instance): print("delete方法") class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 def __init__(self,n): self.x=n# self.x=n等价于b1.x=1就是在给x赋值,而x就是描述符Foo()就会触发Foo的set,第一次赋值 b1=Bar(10)#实例化一个b1的实例 print(b1.__dict__) b1.x=11#重新赋值一次 print(b1.__dict__)#再次查看b1的属性字典 C:\python35\python3.exe D:/pyproject/day28/描述符.py set方法 <__main__.Bar object at 0x0000000000B64588> 10 {'x': 10} set方法 <__main__.Bar object at 0x0000000000B64588> 11 {'x': 11}
7、描述符的优先级
1、类属性
2、数据描述符
3、实例属性
4、非数据描述符
5、找不到的属性触发__getattr__()
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法",instance,value)#查看下instance和value是什么 def __delete__(self, instance): print("delete方法") class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 b1=Bar()#实例化一个实例b1 b1.x#调用b1的x print(b1.x)#调用b1的x,打印b1.x的返回值 C:\python35\python3.exe D:/pyproject/day28/描述符优先级.py get方法 get方法 None
7.1类属性优先级最高
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法",instance,value)#查看下instance和value是什么 def __delete__(self, instance): print("delete方法") class Bar: x=Foo()#定义了一个Bar的数据属性x,值是Foo实例化的一个结果 Bar.x=22#定义一个类的数据属性x,定义到了Bar这个类的__dict__属性字典里面 b1=Bar()#实例化一个实例b1 print(b1.x)#实例b1没有x,所以找类,类里面的x的就是22,跟描述符现在没有关系了,因为已经在类里面找到了 C:\python35\python3.exe D:/pyproject/day28/描述符优先级.py 22
7.2如果没有定义类属性,就是数据描述符第二
class Foo: def __get__(self, instance, owner): print("get方法") def __set__(self, instance, value): print("set方法",instance,value)#查看下instance和value是什么 def __delete__(self, instance): print("delete方法") class Bar: x=Foo()#定义了一个数据描述符x # Bar.x=22#定义一个类的数据属性x b1=Bar()#实例化一个实例b1 print(Bar.__dict__)#查看类的属性字典, print(b1.x)#实例b1没有x,所以找类,类里面的x的就是Foo这个对象,所以就触发了get方法 C:\python35\python3.exe D:/pyproject/day28/描述符优先级.py {'x': <__main__.Foo object at 0x0000000000D3C748>, '__module__': '__main__', '__doc__': None, '__dict__': <attribute '__dict__' of 'Bar' objects>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>} get方法 None
7.3实例属性优先级大于非数据描述符,什么是非数据描述符,就是没有__set__
class Foo: def __get__(self, instance, owner): print("get方法") class Bar: x=Foo()#现在这个x就是一个非数据描述符 b1=Bar() b1.x=1#给实例设置一个x的属性值为1 print(b1.__dict__)#发现设置到了b1的属性字典里面,并没有触发Foo的set方法,因为就没有set C:\python35\python3.exe D:/pyproject/day28/描述符优先级.py {'x': 1}