上一篇文章中,我们通过使用property特性,从而以一种兼容性更好的方式对有缺陷的类型设计的代码进行了优化。
在本文中,我们将继续对property做一个补充,同时看这种解决方案在Python中更抽象意义上的泛化之后的特性——属性描述符的用法。
再看property其实,关于property的使用,对应着面向对象中对实例属性的三种操作:
1、@property修饰的方法:将相关的私有属性封装为只读的特性,对应get操作。
2、@方法名.setter修饰的同名方法:提供对应的私有属性的设置接口,对应set操作。
3、@方法名.deleter修饰的同名方法:提供对应的私有属性的删除接口,对应delete操作。
前两点在上一篇的文章中其实已经有所提及,下面以一个完整的代码来演示一下property的完整使用:
执行结果:
可以看到我们分别执行对age特性的访问、设置、删除时,会调用对应的property修饰的方法。
通过property修饰的方法对属性的封装,我们可以对属性的各种操作进行自定义的控制。但是,如果我们有多个属性,都要进行类似的自定义控制的功能,每个属性都要写3个同样的封装方法,似乎有点麻烦。
根据DRY(Don't Repeat Yourself)原则,以及Python的设计理念,必然有更加便捷的方法,帮我们快速实现对一批属性进行类似property特性的封装,这就是“属性描述符”。
属性描述符property这种属性封装的方式进一步泛化,就得到了Python中的属性描述符的概念。而所谓的属性描述符就是一个代表属性值的对象,它通过实现一个或者多个魔术方法__get__()、__set__()和__delete__(),可以将描述符与属性访问机制进行挂钩,从而自定义对属性的访问控制。
简单看一下,上面通过property封装age属性的方式,使用描述符的方式的实现代码:
执行结果:
我们简单分析一下,可以得出以下结论:
1、从执行效果来看,使用描述符的方式跟property的方式效果是一致的。
2、属性描述符的方式,需要定义一个类,实现__get__()、__set__()、__delete__()这三个魔术方法。
3、属性描述符的方式中,将属性描述符要控制的属性,以类属性的形式定义,类属性指向的为属性描述类的实例对象,无需再定义私有属性了,这种方式似乎更加彻底,从类型实例对象的__dict__中可以看到,并不存在该属性。
此外,property本身是描述符的一种特殊形式,它的背后也是使用了描述符机制。
但是,如果只是上面的这个代码示例,似乎没有看出使用描述符的好处,而且代码变得更加难以阅读。如果在类型中新增了几个属性,也需要进行同样的访问控制呢?
此时,如果使用property方式,对应的代码,有几个属性需要同样的控制,就要写几次。而如果使用描述符的方式,只需要在类型的定义中添加几个同样的类属性即可。假如我们要添加两个属性:工龄、司龄,使用同样的访问控制,直接来看实现代码:
执行结果:
可以看到,添加了两个属性,我们只需要在类中添加两个类属性,指向描述符实例对象,即自动完成了同样的属性访问控制。
总结
本文完整地介绍了property的用法及其对应的get、set、delete的三种操作的方法的封装。并介绍了相较于property更加通用的属性封装的方法——属性描述符。需要说明的是:
1、property是描述符的一种特殊形式。
2、描述符提供了更大的灵活性和访问控制力,但是property提供的接口一般来说更加直观易读,且更加简洁。
3、在使用中,如果只有少量简单的属性需要加访问控制,可以选择使用property来实现,从而更加直观易读;但是,如果涉及到多个属性需要进行访问控制,则可以考虑使用描述符。
此外,常见的ORM框架,也是基于描述符的特性来实现的,感兴趣的可以查阅相关的源码。
感谢您的拨冗阅读,本文的内容如果对您学习Python有所帮助,欢迎点赞、收藏。