• 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

  • 相关阅读:
    spring是什么?
    检查80端口是否被占用的命令是: netstat -ano | findstr 0.0.0.0:80 或 netstat -ano | findstr "80"
    配置Dubbo 项目遇到的坑---无法读取方案文档 'http://code.alibabatech.com/schema/dubbo/dubbo.xsd'
    maven中使用tomcat插件启动,显示build success但无法继续启动的解决方法
    java.io.File类操作
    eclipse将web项目打war包部署到tomcat下
    Mapper.xml中的resultMap
    2.PLSQL编写简单存储过程(传入参数,修改+打印)
    1.PLSQL编写简单存储过程(无参,纯打印)
    select2
  • 原文地址:https://www.cnblogs.com/z-x-y/p/10216249.html
Copyright © 2020-2023  润新知