描述器几乎支撑了整个python面向对象有三个方法依然跟实例有关
get,set__,delete,del留给析构函数了,这里只能用delete了
del(self)只能由一个参数
delete(self,item)这里就有两个参数
加个print看看到底谁调用谁
有几个参数,现在这么直接调用是不行的
调用构造器了
24行就有输出的东西,这样类跟实例就建立关系了
打印出a说明确实是实例的
还有一种方式,下面是通过类属性按照顺序找到a的实例调用
走到这里,instance是none,owner是B
再注释中间的
B.x访问x=A()
就跑到这里打印自己self,就用到了repr格式打印
打印整个看看
调用a1是none,B.x返回的对象调用a1,get是拿东西,看你没return就出问题了
return self
这样就没问题了
这个方法,需要在这里构建两个类,B类属性指向另一个类的实例,当你访问这个类属性,相当于在访问类的实例,访问这个实例的时候,如果提供了__get__方法,就回来访问这个方法,B.X就是这么触发的,B.X.A1等于对它的B.X的返回值进行访问,所以等于A()实例.a1
owner属主这边的访问才会触发这个方法,self是A的实例
再换一种写法,两次实例化
现在打开一下类属性
查看里面三个参数各是什么
如果使用B的实例来访问,这里就显示B的实例
现在看到的就是描述器的get方法,需要有2个类构成,其中一个类的类属性必须是另一个类的实例,在那个类中还要提供__get__方法
注释掉运行起来也没什么问题
加了这个其实提供了一种编程的手段,访问的时候加一道拦截,做一些相应的处理,比如在owner上加个类属性上去
你想要这个实例有什么能力,这个x就有什么能力,本来属性是1234,现在可以描述成一个实例,,类有多强大,这个属性有多强大
查看一下set
set
没有走到set里面
B。x已经被覆盖掉了
换一种方式试试,这样好像有效果
进去了
赋值即定义,查看下字典里到底是什么
x:A100,代表还是A的实例
这一覆盖就什么都没了
已经不是A的实例了,就不能那个用A的get方法
如果调用instance就是属主的实例也送进来
A实例self,属主实例instance,
赋值成功,等于赋值给B.X的返回到a1属性,改成2,所以这里的X实例是没有变化的
把实例搞清楚就可以
试试删除delete
x确实被删掉了
确实能够删除
这是对三个方法的简单尝试
self在谁里面就是谁的实例
instance跟owner相关,owner是属性的所属类
B的实例在不在,都不影响A的实例进行访问,大不了instance没有
加入了get方法
定义了__get__,set,__delete__的这个类,称为描述器,对于类B或者类B的实例的x属性读取,称为对类A的实例的访问,就会调用__get__方法、
解决报错,get方法有返回值
试试用B的实例来访问
现在来访问看看,实例属性有没有就一目了然了
现在测的很清楚,看到的描述器效果,似乎在实例上复制,通过这样访问的方式,虽然可以访问到实例,但是根本就不会调用get和set方法
如果是使用b.x=就一定会触发get和set方法
描述器是两个类和实例在一起,很麻烦
b.b是没法触发get方法的
python中一个类,实现了get,set,,delete方法,其中的任意一个方法,就是描述器,但是一般都会有get方法,如果仅仅是get,就称为非数据描述器,如果实现了get方法之外的,set和delete 2个之一叫数据描述器
非数据描述器和数据描述器表现的状况是不一样的
如果一个类的类属性设置为描述器实例,那么它被称为owner属主
属性的访问顺序,如果写了getattribute,是它,如果允许采用默认顺序走,就是object,这时候的顺序就是先实例子弟,然后是类字典,往祖先类找,如果找到object都没有,就需要抛出异常了,如果实现了getattr魔术方法,就交给它,暂时不抛出属性异常,除非自己抛出异常,否则异常就没有了
试试加了描述器会怎么样
X=A()访问到A里面
现在print(b.x)打印什么结果
现在这么说,描述器虽然加了,但是对于实例来讲,优先使用自己的字典
这样看看打印什么,访问的是A的这个实例
只有非数据描述器,只有get,现在测试下来,B.x跟实例无关,该怎么去访问就去怎么访问,因为是描述器,就触发 了get方法,但是使用实例来访问优先使用自己的字典
只要get+set/delete就变成数据描述器
提示set属性没有设置,说明找到set上去了
打印这个字典做了几件事情
先打印的这边
set方法里没有赋值,实例字典里现在是空的
多了set是数据描述器,有了数据描述器,跟刚才的方式有点不同,非数据描述器是优先使用了实例的字典里面的数据,如果有赋值语句会在自己的字典里有的(self.x=1000
如果有了非数据描述器,就不会通过self.x=1000这样的语句加进去
上面是数据描述器,self.x=setattr(self,’‘x’’),这两个一样,就试试self.dict[’‘x’’],(其实不推荐这么写)
这样就写进去了
如果是数据描述器,访问属性还是按照描述器的方式来找,数据描述器优先使用描述器的值
访问属性还是按照描述器的方式来找,数据描述器优先使用描述器的值
把set注释掉,试试打印多少
**就变成2000了,当如果用非数据描述器,优先用实例自己的
**如果用数据描述器,就无视字典,直接使用描述器的来访问
所谓的属性相同就是类属性和实例属性同名,访问的时候实例没有就看类有没有,然后用类访问实例,类没有实例就不能访问
以前是如果同名。实例访问肯定是实例字典优先,因为看的是自己的字典,使用了数据描述器情况就改变了
实例的__dict__优先于非数据描述器,数据描述其优先于实例的__dict__
描述器是由两个类和其中一个类实例来产生关系,下面从属主类里面,通过实例来访问,用类来get是可以的
B。x相当于直接访问类,直接把x覆盖了
描述器影响了属性的搜索顺序,数据描述器比实例字典优先,但是一般getattribute要加入进来,但是一般getattr和get描述器不会混着用
可以从B的类来访问描述器,描述器少不了,get,set,delete
可以动态把实例z属性塞进去,相当于在B实例上增加z属性
z=1000该怎么访问怎么访问
使用非属性访问方式
不建议这么玩,除非特别明确,还是大部分使用属性
数据描述器的优先级是比较高的,只要对属性访问就在这里操作,如果使用非数据描述器,是低于属性字典的