python数据描述符,python数据描述符

本文详细介绍了Python中的描述符,这是一种创建托管属性的方法,用于保护属性不受修改、进行类型检查。描述符通过实现__get__,__set__,__del__方法来控制属性的访问和操作。通过一个示例展示了如何使用描述符来实现属性的类型检查,从而避免动态类型语言的潜在问题。描述符是Python中实现属性封装和验证的有效工具。
摘要由CSDN通过智能技术生成

标签:

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 "", line 1, in

t.name = 3

File "", 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

标签:

来源: https://www.cnblogs.com/z-x-y/p/10216249.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值