描述器的概念:就是描述一个属性操作的对象,比如说有一个person类,有一个属性是age,我们把age属性的改,删,查都封装到了一个对象里面,其实这个对象就是描述器;
描述器存在的意义:其实就是为了我们能更方便的操控数据,达到保护数据,过滤数据的目的;
描述器的定义方式1:其实就是我们之前学过的property,我们用property就可以把操作属性的方法进行包装并且返回一个对象,我们通过操作这个对象,这个对象就会调用属性的删改查方法,达到操作属性的目的:从下面的代码种我们可以清楚的看到age存的是属性对象,和普通的属性是不一样的;
![](https://img.haomeiwen.com/i5895872/df57b650bdbb7a88.png)
我们在看看用装饰器写的描述器:可以看出装饰器也是达到同样的效果
![](https://img.haomeiwen.com/i5895872/a8e8ec17e0011005.png)
描述器定义方式2:我们从描述器的定义下手,就是把属性操作封装在一个对象里面,也就是一个类里面,那我们先新建一个类,然后把属性操作的方法写出来,分别是get,set以及delete,然后我们在把这个对象实例化出来,由此我们就可以操作这个对象来实例对属性的操作了:
我们来分析一下下面的代码:首先有一个Person类,这个类有一个类属性是指向Age()实例,然后有一个Age类,Age类有三个方法,我们把三个方法接收的参数都打印出来了,self接收的是age实例,instance接收的是person实例,那么我们到底把传过来的值存在self还是instance呢?我们先来看赋值过程,我们实例出了两个Person实例p1和p2,然后给age属性赋值,我们知道age属性是类属性,不管我们实例出多少个实例,都是公用同一个age属性,而age指向Age()实例,意思就是说self是同一个,我们当然不能把不同的数据存放在同一个self里面,而是应该存在不同的实例里面,所以我们应该存放在instance里面,而且使用这种方法我们只需要重写一次get,set和delete方法,针对Person的不同属性,我们只需要重新创建新的对象即可,所以这种方式比property更加便捷;
![](https://img.haomeiwen.com/i5895872/402223831edb243b.png)
描述器的注意事项:
1.我们知道age属性属于类属性,那么当我们使用描述器的时候,究竟应该用类去调用,还是用实例去调用呢?用什么去调用,主要是看有没有执行描述器的3个方法,下面是测试代码:
![](https://img.haomeiwen.com/i5895872/d1ac28eb7f7a1b23.png)
![](https://img.haomeiwen.com/i5895872/7056fbf239c2d2bf.png)
从上所示,我们知道当使用描述器的时候我们应该使用实例去调用;
2.我们知道类有分为新式类和经典类,那描述器对类的不同有什么区分呢?我们只需要分别使用新式类和经典类去定义类就行了,这里我就不在继续贴代码了,直接给答案:就是我们的描述器和宿主类都必须是新式类才能使用描述器;
3.描述器的方法拦截:首先我们来看一下查找实例属性的顺序,a.通过实例的__dict__查找,b.通过类的__dict__查找,c.通过父类的__dict__查找,d.__getattr__方法,前面的我们都很清楚,我们只验证最后一个:
![](https://img.haomeiwen.com/i5895872/0dd859a42d35585a.png)
我们可以看到里面并没有查找__get__方法,那么这个方法在哪执行的呢,其实是通过__getattribute__实现的,在查找的开始,如果解释器发现了实现了描述器的__get__方法就会通过__getattribute__方法来调用__get__方法,并且__getattribute__这个方法是底层自动调用的,我们不需要做什么,但是如果我们重写了__getattribute__这个函数,那么__get__方法就会被屏蔽掉,
![](https://img.haomeiwen.com/i5895872/d29082c0e51c2b0f.png)
4.描述器和实例属性同名时的优先级:首先我们补充一下资料描述器和非资料描述器的概念,资料描述器的概念是描述器既实现了get方法,也实现了set方法,而非资料描述器是仅仅实现get方法,那么我们的优先级就是资料描述器>实例属性>非资料描述器;
![](https://img.haomeiwen.com/i5895872/5a0e36ae539a9ed3.png)
![](https://img.haomeiwen.com/i5895872/7abd87bc1bc07f5b.png)
补充一点装饰器的类实现:我们知道装饰器就是在不改变自身的情况下增加其他功能,之前学过了方法装饰器,其实类装饰器也是一样的道理,直接上代码:下面的代码是没有用语法糖实现的,当然我们也可以使用语法糖操作,和之前的函数装饰器一样的,直接用@类名即可;
![](https://img.haomeiwen.com/i5895872/e0de5271c6dc87b8.png)
网友评论