python 描述_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。

classTest(object):

name= None

正常情况下,name的值(其实应该是对象,name是引用)都应该是字符串,但是因为Python是动态类型语言,即使执行Test.name = 3,解释器也不会有任何异常。当然可以想到解决办法,就是提供一个get,set方法来统一读写name,读写前添加安全验证逻辑。代码如下:

classtest(object):

name=None

@classmethoddefget_name(cls):returncls.name

@classmethoddefset_name(cls,val):ifisinstance(val,str):

cls.name=valelse:raise TypeError("Must be an string")

虽然以上代码勉强可以实现对属性赋值的类型检查,但是会导致类型定义的臃肿和逻辑的混乱。从OOP思想来看,只有属性自己最清楚自己的类型,而不是它所在的类,因此如果能将类型检查的逻辑根植于属性内部,那么就可以完美的解决这个问题,而描述符就是这样的利器。

为name属性定义一个(数据)描述符类,其实现了__get__和__set__方法,代码如下:

classname_des(object):def __init__(self):

self.__name =Nonedef __get__(self, instance, owner):print('call __get__')return self.__name

def __set__(self, instance, value):print('call __set__')ifisinstance(value,str):

self.__name =valueelse:raise TypeError("Must be an string")

测试类如下

classtest(object):

name= name_des()

测试代码及输出结果如下

>>> t =test()>>>t.name

call__get__

>>> t.name = 3call__set__Traceback (most recent call last):

File"", line 1, in t.name= 3File"", 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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值