描述器
描述器的表现
- 用到3个魔术方法:__get__()、__set__()、__delete__()
- 方法用法:
- object.__get__(self,instance,owner)
- object.__set__(self,instance,value)
- object.__delete__(self,instance)
- self指代当前实例,调用者
- instance是owner的实例
- owner是属性所属的类
- 看下面代码,思考执行流程。
- 结论:
- 类加载的时候,类变量需要先生成,而类B的x属性是类A的实例,所以类A先初始化,所以打印A.init
- 然后执行到B.x.a1.然后打印实例化并初始化B的实例b
- 打印b.x.a1,会查找属性b.x,指向A的实例,所以返回A实例的属性a1的值
- 因为定义了__get__方法,类A就是一个描述器,对类B或者类B的实例的x属性读取,成为对类A的实例的访问,就会调用get方法
- 解决上面方法中B.x.a1抛错问题,报错是因为添加get方法后造成的,get三个参数的的含义:
- slef都是A的实例
- owner都是B类
- instance说明:
- None表示没有B类的实例,对应调用B.x
- <__main__.B object at 0x000000310E738828>表示时B的实例,对应调用B().x
- 使用返回值解决,返回self,就是A的实例,该实例有a1属性,返回正常
结论:只有类属性是类的实例才可以触发get
描述器定义
- Python中,一个类实现了__get__、__set__、__delete__三个方法中的任何一个方法,就是描述器
- 如果仅实现了__get__,就是非数据描述符non-data descriptor
- 同时实现了__get__、__set__就是数据描述符data descriptor
- 属性的访问顺序
- b.x访问到了实例的属性,而不是描述器
- 下面代码为A类增加set方法
结论:属性查找顺序:实例的__dict__优先于非数据描述器数据描述器 优先于实例的__dict____delete__方法有同样的效果,有了这个方法,就是数据描述器
- 增加b.x = 500,这是调用数据描述器的__set__方法,或调用那个非数据描述器的实例覆盖
- B.x = 600,赋值即定义,这是覆盖类的属性。类的字典更新
本质(进阶)
- python真的会做这么复杂吗,再来一套属性查找顺序规则?看看非数据描述器和数据描述器,类B及其__dict__的变化
- 屏蔽和不屏蔽__set__方法,看看变化
- 原来不是什么数据描述器优先级高,而是把实例的属性从__dict__中去掉,造成了该属性如果是数据描述器优先访问的假象
- 说到底,属性访问顺序从来没有变过
Python中的描述器
- 描述器在python中广泛应用
- python的方法(包括staticmethod()和classmethod())都实现为非数据描述器,因此,实例可以重新定义和覆盖方法。这允许单个实例获取与同一类的其他实例不同的行为?
- property()函数实现为一个数据描述器,因此,实例不能覆盖属性的行为。
- foo、bar都可以在实例中覆盖,z不可以
- A.clsmtd()的意思就是None(),一定报错,如何改?
- A.clsmtd()其实应该是A.clsmtd(cls)(),应该怎么处理?
- A.clsmetd = A.clsmtd(cls)
- 用partial函数
- 对实例的数据进行校验
- 对上面类的实例的属性,name、age进行数据校验
- 方法一:写函数,在init中检查如果不合格,直接抛异常
- 方法二:装饰器,使用inspect模块完成
- 方法三描述器
本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/89076