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 ...
随机推荐
- 设置两个WdatePicker的开始时间小于结束时间,结束时间大于开始时间
contract_start_date_id为开始时间ID contract_end_date_id为结束时间ID $("#contract_start_date_id").bin ...
- c#后台修改前台DOM的css属性示例代码
<div id = 'div1' runat="server">haha</div> ----------- 后台代码中这样调用 div1.Style[&q ...
- DecimalFormat 四舍五入Float类型的坑
今天又踩了一个坑,使用DecimalFormat来完毕四舍五入.可是传入的是float类型,几轮測试才发现一个问题,传入的float会被转为double类型.大家都知道float是4位,double是 ...
- 工作中php处理HTTP请求的缺陷总结
之前遇到过php在处理一部分业务的时候总是感觉有短板,有些东西总是不能随人心意.比如执行时间问题,一个进程需要读取多条数据放入数组循环执行处理流程,就会很慢,容易超时,这个超时问题 可以用php设置s ...
- C语言字符串的输入输出
字符串的输出 在C语言中,输出字符串的函数有两个: puts():直接输出字符串,并且只能输出字符串. printf():通过格式控制符 %s 输出字符串.除了字符串,printf() 还能输出其他类 ...
- Spring学习笔记--自动检测
要使用自动检测,我们需要用到<context:annotation-scan>标签.<context:annotation-scan>元素除了完成与<context:an ...
- Linux安装好系统后优化
author:headsen chen date: 2018-05-14 15:35:49 1.快速更改国内yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc ...
- LVS负载均衡服务
LVS负载均衡服务 LVS负载均衡调度技术是在Linux内核中实现的,因此被称为Linux虚拟服务器.使用LVS时,不能直接配置内核中的ipvs,而需要使用ipvs的管理工具ipvsadm进行管理. ...
- docker-compose安装elasticsearch集群
文件目录: 1.编写docker-compose文件 version: '3' services: es-master: image: elasticsearch:6.4.3 container_na ...
- 160505、oracle 修改字符集 修改为ZHS16GBK
修改oracle字符集 方法/步骤 oracle数据库的字符集更改 A.oracle server 端 字符集查询 select userenv('language') from dual 其中N ...