python数据描述符
Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题苦恼的朋友提供一个思考问题的参考,由于个人能力有限,文中如有笔误、逻辑错误甚至概念性错误,还请提出并指正。
2、什么是描述符
Python 2.2 引进了 Python 描述符,同时还引进了一些新的样式类,但是它们并没有得到广泛使用。Python 描述符是一种创建托管属性的方法。描述符具有诸多优点,诸如:保护属性不受修改、属性类型检查和自动更新某个依赖属性的值等。
说的通俗一点,从表现形式来看,一个类如果实现了__get__,__set__,__del__方法(三个方法不一定要全部都实现),并且该类的实例对象通常是另一个类的类属性,那么这个类就是一个描述符。__get__,__set__,__del__的具体声明如下:
__get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)
其中:
__get__ 用于访问属性。它返回属性的值,或者在所请求的属性不存在的情况下出现 AttributeError 异常。类似于javabean中的get。
__set__ 将在属性分配操作中调用。不会返回任何内容。类似于javabean中的set。
__delete__ 控制删除操作。不会返回内容。
注意:
只实现__get__方法的对象是非数据描述符,意味着在初始化之后它们只能被读取。而同时实现__get__和__set__的对象是数据描述符,意味着这种属性是可读写的。
3、为什么需要描述符
因为Python是一个动态类型解释性语言,不像C/C++等静态编译型语言,数据类型在编译时便可以进行验证,而Python中必须添加额外的类型检查逻辑代码才能做到这一点,这就是描述符的初衷。比如,有一个测试类Test,其具有一个类属性name。
class Test(object):
name = None
正常情况下,name的值(其实应该是对象,name是引用)都应该是字符串,但是因为Python是动态类型语言,即使执行Test.name = 3,解释器也不会有任何异常。当然可以想到解决办法,就是提供一个get,set方法来统一读写name,读写前添加安全验证逻辑。代码如下:
class test(object):
name = None
@classmethod
def get_name(cls):
return cls.name
@classmethod
def set_name(cls,val):
if isinstance(val,str):
cls.name = val
else:
raise TypeError("Must be an string")
虽然以上代码勉强可以实现对属性赋值的类型检查,但是会导致类型定义的臃肿和逻辑的混乱。从OOP思想来看,只有属性自己最清楚自己的类型,而不是它所在的类,因此如果能将类型检查的逻辑根植于属性内部,那么就可以完美的解决这个问题,而描述符就是这样的利器。
为name属性定义一个(数据)描述符类,其实现了__get__和__set__方法,代码如下:
class name_des(object):
def __init__(self):
self.__name = None
def __get__(self, instance, owner):
print('call __get__')
return self.__name
def __set__(self, instance, value):
print('call __set__')
if isinstance(value,str):
self.__name = value
else:
raise TypeError("Must be an string")
测试类如下
class test(object):
name = name_des()
测试代码及输出结果如下
>>> t = test()
>>> t.name
call __get__
>>> t.name = 3
call __set__
Traceback (most recent call last):
File "<pyshell#99>", line 1, in <module>
t.name = 3
File "<pyshell#94>", line 12, in __set__
raise TypeError("Must be an string")
TypeError: Must be an string
>>> t.name = 'my name is chenyang'
call __set__
>>> t.name
call __get__
'my name is chenyang'
>>>
从打印的输出信息可以看到,当使用实例访问name属性(即执行t.name)时,便会调用描述符的__get__方法(注意__get__中添加的打印语句)。当使用实例对name属性进行赋值操作时(即t.name = 'my name is chenyang.'),从打印出的'call set'可以看到描述符的__set__方法被调用。熟悉Python的都知道,如果name是一个普通类属性(即不是数据描述符),那么执行t.name = 'my name is chenyang.'时,将动态产生一个实例属性,再次执行t.name读取属性时,此时读取的属性为实例属性,而不是之前的类属性(这涉及到一个属性查找优先级的问题,下文会提到)。
至此,可以发现描述符的作用和优势,以弥补Python动态类型的缺点。
转自https://www.cnblogs.com/chenyangyao/p/python_descriptor.html
python数据描述符的更多相关文章
- python - 数据描述符(class 内置 get/set/delete方法 )
数据描述符(class 内置 get/set/del方法 ): # 什么是描述符 # 官方的定义:描述符是一种具有“捆绑行为”的对象属性.访问(获取.设置和删除)它的属性时,实际是调用特殊的方法(_g ...
- python小知识-属性查询优先级(如果有同名类属性、数据描述符、实例属性存在的话,实例>类>数据描述符)
https://www.cnblogs.com/Jimmy1988/p/6808237.html https://segmentfault.com/a/1190000006660339 https:/ ...
- Python的描述符
1.描述符的定义 描述符是与特定属性互相绑定的一种协议,通过方法被触发修改属性,这些方法包括__get__(),__set__(),__delete__().将这些方法定义在类中,即可实现描述符 2. ...
- Python 属性描述符和属性的查找过程
属性描述符可以用来控制给属性赋值的时候的一些行为 import numbers class IntField: def __get__(self, instance, owner): return s ...
- [py]数据描述符优先级
实例查找属性的顺序: 类属性 > 数据描述符 > 实例属性 > 非数据描述符 > __getattr__ 类属性>数据描述符>实例属性 class Str: def ...
- python tips:描述符descriptor
描述符(descriptor)是实现了__get__.__set__.__del__方法的类,进一步可以细分为两类: 数据描述符:实现了__get__和__set__ 非数据描述符:没有实现__set ...
- python 属性描述符
import numbers class IntField: # 一个类只要实现了这个魔法函数,那么它就是属性描述符 #数据描述符 def __get__(self, instance, owner) ...
- 一文掌握 Python 的描述符协议
描述符介绍 描述符本质就是一个新式类,在这个新式类中,至少要实现了__get__(),__set__(),__delete__()中的一个.这也被称为描述符协议. class Myclass(obje ...
- Python属性描述符
实现了__get__.set.__delete__中任意一个方法的类,称之为属性描述符. 属性描述符可以控制属性操作时的一些行为. 只要具有__get__方法的类就是描述符类. 如果一个类中具有__g ...
随机推荐
- 图像处理之二维码生成-qr
Javascript生成二维码(QR) 网络上已经有非常多的二维码编码和解码工具和代码,很多都是服务器端的,也就是说需要一台服务器才能提供二维码的生成.本着对服务器性能的考虑,这种小事情都让服务器 ...
- 【BZOJ】1674: [Usaco2005]Part Acquisition(spfa)
http://www.lydsy.com/JudgeOnline/problem.php?id=1674 想法很简单...将每一种看做一个点,如果i可以换成j,那么连边到j.. 费用都为1.. 然后拥 ...
- mac zsh选择到行首的快捷键
Mac OS X 下zsh切换窗口的快捷键:Shift-Command-←. 移动到当前命令行的行首,使用快捷键[Ctrl][A].移动到当前命令行的行尾,使用快捷键[Ctrl[E].
- 在visual studio中运行C++心得
1.在visual studio中建立C++项目 (1)新建->项目->空项目 C++ (2)右击项目->添加->新建项->C++文件(.app) (3编写C++文件 ...
- 这些孩子在 Ubuntu 的 Linux 终端下玩耍
导读 我发现了一个孩子们在他们的计算机教室里玩得很开心的视频.我不知道他们在哪里,但我猜测是在印度尼西亚或者马来西亚.视频请自行搭梯子: https://youtu.be/z8taQPomp0Y 在L ...
- Java关键字final、static总结
对Java关键字Final和Static进行总结. 一.final 根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类.非抽象类成员方 ...
- JS获取时间戳+C#水煎戳转换
JS获取了当前毫秒的时间戳. var timestamp=new Date().getTime(); //第二种方法: //var timestamp = (new Date()).valueOf() ...
- Javaweb Tomcat 项目部署方式
一.静态部署 1.直接将web项目文件件拷贝到webapps 目录中 Tomcat的Webapps目录是Tomcat默认的应用目录,当服务器启动时,会加载所有这个目录下的应用.所以可以将JSP ...
- 20165330 2017-2018-2 《Java程序设计》第1周学习总结
教材学习内容总结 java的历史,地位,特点. java的平台介绍 java应用程序的开发及源文件的编写规则 java反编译特点 安装JDK Windows上 在安装JDK后设置系统环境变量,因为我的 ...
- C#/java 执行oracle package
使用pl/sql创建package CREATE OR REPLACE PACKAGE FirstPage is type outlist is ref cursor; Procedure p_get ...