描述符

Python的面向对象里面,他的灵活程度是比其他语言更加灵活

  • python面向对象中,类属性,我既可以通过类名访问,也可以通过实例访问
  • python中的类方法(@classmethod装饰的方法),我也可以通过类名访问,也可以通过实例访问

两种关系

  • **关系一:**类成员与实例成员的关系(包括属性和方法这两种成员)
  • **关系二:**父类和子类的关系

对于关系一而言,实例是可以访问类的成员的,但是类不能访问实例成员;对于关系二而言,子类继承父类的类成员和实例成员。

Python一切皆对象,故而都有__dict__属性,包括对象实例、函数、类本身。

对象对属性访问顺序:实例属性–类属性–父类的类属性–__getattr__()方法(访问一个不存在的属性的时候发生的行为)

__getattribute__当一个属性被访问的时候发生的行为,称之为****“属性拦截器****”。Python中只要定义了继承object的类,就默认存在属性拦截器,只不过是拦截后没有进行任何操作,而是直接返回。如果定义了__getattribute__魔法方法,他总是优先调用。

__getattribute__的注意事项

  • 一定要在每一个需要访问的属性里面设置返回值,否则会返回None,一般有两种做法
    • return super(selfClass, self).__getattribute__(key)这种形式
    • return object.__getattribute__(self,key) 即返回父类的__getattribute__方法。
  • 不要再在 __getattribute__方法的定义内部显示使用self.成员 这种方法,这样可能会造成无限递归,导致系统崩溃。

**__getattr__**可以用来在当用户试图访问一个根本不存在(或者暂时不存在)的属性时,来定义类的行为。

**__getattr__**和 __getattribute__的详细区别总结:

(1)__getattribute__称之为“属性、方法拦截器”,不管是属性还是方法,第一步就是先访问__getattribute__;而__getattr__仅仅针对的是属性,不针对方法,即访问未存在的方法的时候依然还是会报错。

(2)__getattribute__针对的是访问已经存在的(属性和方法);**__getattr__**针对的是访问未存在的(属性)。

(3)__getattribute__和**__getattr__虽然针对每一个访问的key,一定要有对应的返回值(参见前文),但是返回的东西却不是一样的,即__getattribute__返回父类的__getattribute__函数,而__getattr__**返回我希望为未知属性设置的那个值或者是异常信息。

**setattr**方法允许定义为某个属性赋值的时候所发生行为,不管这个属性存在与否,都可以对任意属性的任何变化都定义自己的规则。

总结:

只要是属性被修改或者是赋值,不管这个属性是实例属性、类属性、父类的类属性;亦或者是已经存在的属性、不存在的属性,只要是修改和赋值,都会调用到__steattr__方法。

补充:

关于**setattr有一点需要说明,使用它时必须小心,不能写成类似self.name = “张三”这样的形式,因为这样的赋值语句会调用setattr**方法,这样会让其陷入无限递归,参见前文的__getattribute__方法出现无限递归的情况,二者是一样的。

delattr用于处理删除属性时的行为。和setattr方法要注意无限递归的问题,重写该方法时不要有类似del self.name的写法。

注意:__delattr__只能够删除 ****已经存在的、实例属性****,对于不存在的属性和类属性(因为它是属于类的)是不能够删除的。

上面讲到的三个重要的魔术方法__getattr__、setattr、__delattr__这三个方法实现了对属性的的灵活控制,称之为**“属性控制三剑客”**。

什么是描述符——descriptor以及相关的一系列定义

(1)描述符:某个类,只要是内部定义了方法 get, set, delete 中的一个或多个,就可以称为描述符,描述符的本质是一个类。

(2)描述符协议:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),set(),delete()中的一个,这些魔术方法也被称为描述符协议

(3)非数据描述符:一个类,如果只定义了 get() 或者是__delete__()方法,而没有定义 set()方法,则认为是非数据描述符(即没有定义__set__)

(4)数据描述符:一个类,不仅定义了 get() 方法,还定义 set(), delete() 方法,则认为是数据描述符(即定义了__get__和__set__)

(5)描述符对象:描述符(即一个类,因为描述符的本质是类)的一个对象,一般是作为其他类对象的属性而存在
**绑定行为:**所谓的绑定行为,是指在属性的访问、赋值、删除时还绑定发生了其他的事情。

托管属性:python描述符是一种创建“托管属性”的方法,即通过描述符(类)去托管另一个类的相关属性,也可以说是类的属性的一个代理。其实就是专门用一个类去装饰某一个属性,我可以把这个属性定义成任何我想要的样子,所谓的*“一对一定制属性”**。一个类属性****本质*上就是属性描述类的一个实例对象

描述符三个函数的定义形式:

  • def get(self, instance, owner)

    self:指的是描述符类的实例
    instance:指的是使用描述符的那个类的实例
    owner:指的是使用描述符的那个类

  • def set(self, instance, value)

  • def delete(self, instance,)

总结:对于*类属性描述符***,如果解析器发现属性property是一个描述符的话,它能把Class.x转换成Class.__dict__[‘property’].__get__(None, Class)来访问。

类属性描述符

总结:

(1)对于类装饰器属性,只要出现属性访问(不管是通过对象访问还是类名访问),都会优先调用装饰器的__get__方法;

(2)对于类装饰器属性,若出现属性修改(不管是通过对象访问还是类名访问),都会优先调用装饰器的__set__方法;

(3)对于类装饰器属性,若出现属性删除(不管是通过对象访问还是类名访问),都会优先调用装饰器的__delete__方法;

实例属性描述符

**总结:**描述符是一个类属性,必须定义在类的层次上, 而不能单纯的定义为对象属性。

属性的优先访问级别总结

1、如果没有设置“描述符属性”

没有设置描述符属性,则属性的优先访问顺序和我们前面文章里面所讲的是一样的,即

(1) getattribute(), 无条件调用,任何时候都先调用

(2)实例属性

(3)类属性

(4)父类属性

(5) getattr() 方法 #如果所有的属性都没有搜索到,则才会调用该函数

2、如果设置了“描述符属性”

注意:因为描述符属性本身就是定义在类里面的,也可以当成是类属性,但是它并不是一般的类属性,请记住一句话:一旦一个属性被标记为“描述符属性”,那它的性质再也不会变。

但是描述符因为它非常灵活的语法,可以实现一些非常高级的python特性,描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性,不仅如此,描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件。

主要参考:
https://blog.csdn.net/qq_27825451/category_9282212.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值