首页 > 编程语言 >Python语言学习讲解十六:python之描述符__set__和__get__ 等解释

Python语言学习讲解十六:python之描述符__set__和__get__ 等解释

时间:2023-04-25 15:34:50浏览次数:75  
标签:__ set obj attr get descriptor dict class




一、方法:

首先说下python中存在的几种方法:对象方法、静态方法、类方法等,归属权分别为obj、cls、cls

其实可以从他们的参数中就可以看的出来

对象方法参数中含有self,这个类似于C++中的this指针。

静态方法使用@staticmethod来修饰,可以通过类或类的实例对象来调用而已.


    1. >>> class
    2. 
    3.     class_attr = "class_attr" #######类似于C++中类成员、方法,所有对象是共享内存,对象和类都是可以进行调用的。
    4. def __init__(self, value):  
    5. self.obj_attr = value
    6. print self.obj_attr 
    7. 
    8.     @staticmethod   #####静态方法
    9. def static_fn(str): 
    10. print "static_fnis call...say %s"
    11. 
    12. 
    13.     @classmethod   #####类方法
    14. def class_fn(cls, str):  
    15. print "class_fnis call...say %s" % str  
    16.   
    17. def obj_fn(self,str):   #####对象方法
    18.  print "obj_fn is call...say %s" % str    
    19. >>> p=Parent('obj_attr')
    20. obj_attr
    21. >>> Parent.__dict__ 
    {'obj_fn': <function obj_fn at 0x02C44B70>, '__module__': '__main__', 'class_attr': 'class_attr', 'class_fn': <classmethod object at 0x02CB40F0>, 'static_fn': <staticmethod object at 0x02C36F50>, '__doc__': None, '__init__': <function __init__ at 0x02C44B30>}#####可以看到类中所包含的成员 
    >>> p.__dict__
    {'obj_attr': 'obj_attr'}#####可以看到对象中所包含的成员,类成员对象并没有在对象的dict中 22. >>> id(p.class_attr)
    46362560#####在看用对象调用类成员的地址输出 23. >>> id(Parent.class_attr)
    46362560#####在看用类调用类成员的地址输出,神奇的发现居然地址是一样的。那就是验证了上面所24. 说的对象其实都是使用类的对象的地址。所有对象在没有修改自己类对象成员的时候都是指向类的初始地址。
    25. >>> p.class_attr = "change_attr"#####我们使用对象修改了类成员属性,然后查看下具体的情况和地址变化
     >>> p.class_attr
    'change_attr' #####对象属性变化了,为何那???原因在于进行此操作的时候,会再对象中添加属于对象26. 的成员change_attr,而且是用新的内存地址,于类的相互不影响
    >>> Parent.class_attr
    'class_attr'类属性无变化了
    >>> id(p.class_attr)
    46212960 #####内存地址变化了 
    >>> id(Parent.class_attr)
    4636256027. >>> p.__dict__
    {'class_attr': 'change_attr', 'obj_attr': 'obj_attr'}#####成员新增了:change_attr28. >>> Parent.class_attr = "p_change_attr"
     >>> p.class_attr
    'change_attr' #####可见当对象的成员发生了地址新的变化后,修改类成员引用的地址,不会影响对象的成员。如果29. 对象此时没有自己的类成员空间,那么类修改类成员空间,则对象的类成员也会发生地址空间变化。
    30.


    属性可以分为两类,一类是Python自动产生的,如__class__,__hash__等,另一类是我们自定义的,如上面的hello,name。我们只关心自定义属性。


    类和实例对象(实际上,Python中一切都是对象,类是type的实例)都有__dict__属性,里面存放它们的自定义属性(对与类,里面还存放了别的东西)。


    有些内建类型,如list和string,它们没有__dict__属性,所以没办法在它们上面附加自定义属性。


    class_attr 属性Parent.class_attr 和Parent.__dict__['class_attr']是完全一样的。因为查找的地方就是__dict__字典中。默认__dict__省略了。没啥神奇的~~~


    Python代码  

    Python语言学习讲解十六:python之描述符__set__和__get__ 等解释___set__和__get__



    1. >>> Parent.class_attr
    2. 'p_change_attr'
    3. >>> Parent.__dict__['class_attr']  
    4. 'p_change_attr'
    5. >>>
    1.  


    obj_fn,情形就有些不同了


    Python代码  

    Python语言学习讲解十六:python之描述符__set__和__get__ 等解释___set__和__get__



    1. >>> Parent.obj_fn
    2. <unbound method Parent.obj_fn>  
    3. >>> Parent.__dict__['obj_fn']  
    4. <function obj_fn at 0x00C3AD70>  
    5. >>>


    Parent.obj_fn是个unbound method。而Parent.__dict__['obj_fn']是个函数(不是方法)。

    Parent.obj_fn针对的是对象的调用。



      1. >>> p.obj_fn
      <bound method Parent.obj_fn of <__main__.Parent instance at 0x02C2EE90>>2.


      是一个bound method。


      关于unbound和bound到还好理解,我们不妨先作如下设想:方法是要从实例调用的嘛(指实例方法,classmethod和staticmethod后面讲),如果从类中访问,如Parent.obj_fn,obj_fn没有和任何实例发生联系,也就是没绑定(unbound)到任何实例上,所以是个unbound,对

      1. 的访问方式,obj_fn和p发生了联系,因此是bound。

      一切的魔法都源自今天的主角:descriptor

       

      p.class_attr,如果Python发现这个属性class_attr有个__get__方法,Python会调用class_attr的__get__方法,返回__get__方法的返回值,而不是返回class_attr(这一句话并不准确,我只是希望你能对descriptor有个初步的概念)。

      Python中iterator(怎么扯到Iterator了?)是实现了iterator协议的对象,也就是说它实现了下面两个方法__iter__和next()。类似的,descriptor也是实现了某些特定方法的对象。descriptor的特定方法是__get__,__set__和__delete__,其中__set__和__delete__方法是可选的。iterator必须依附某个对象而存在(由对象的__iter__方法返回),descriptor也必须依附对象,作为对象的一个属性,它而不能单独存在。还有一点,descriptor必须存在于类的__dict__中,这句话的意思是只有在类的__dict__中找到属性,Python才会去看看它有没有__get__等方法,对一个在实例的__dict__中找到的属性,Python根本不理会它有没有__get__等方法,直接返回属性本身。descriptor到底是什么呢:简单的说,descriptor是对象的一个属性,只不过它存在于类的__dict__中并且有特殊方法__get__(可能还有__set__和__delete)而具有一点特别的功能,为了方便指代这样的属性,我们给它起了个名字叫descriptor属性。


      Python代码  

      Python语言学习讲解十六:python之描述符__set__和__get__ 等解释___set__和__get__


      1. class
      2. def __get__(self, obj, type=None):  
      3. return 'get', self, obj, type  
      4. def __set__(self, obj, val):  
      5. print 'set', self, obj, val  
      6. def __delete__(self, obj):  
      7. print 'delete', self, obj

      这里__set__和__delete__其实可以不出现,不过为了后面的说明,暂时把它们全写上。

      下面解释一下三个方法的参数:

      self当然不用说,指的是当前Descriptor的实例。obj值拥有属性的对象。这应该不难理解,前面已经说了,descriptor是对象的稍微有点特殊的属性,这里的obj就是拥有它的对象,要注意的是,如果是直接用类访问descriptor(别嫌啰嗦,descriptor是个属性,直接用类访问descriptor就是直接用类访问类的属性),obj的值是None。type是obj的类型,刚才说过,如果直接通过类访问descriptor,obj是None,此时type就是类本身。

      三个方法的意义,假设T是一个类,t是它的一个实例,d是T的一个descriptor属性(牛什么啊,不就是有个__get__方法吗!),value是一个有效值:

      读取属性时,如T.d,返回的是d.__get__(None, T)的结果,t.d返回的是d.__get__(t, T)的结果。

      设置属性时,t.d = value,实际上调用d.__set__(t, value),T.d = value,这是真正的赋值,T.d的值从此变成value。删除属性和设置属性类似。

      真正详细的属性查找策略 了,对于obj.attr(注意:obj可以是一个类):

      1.如果attr是一个Python自动产生的属性,找到!(优先级非常高!)

      2.查找obj.__class__.__dict__,如果attr存在并且是data descriptor,返回data descriptor的__get__方法的结果,如果没有继续在obj.__class__的父类以及祖先类中寻找data descriptor

      3.在obj.__dict__中查找,这一步分两种情况,第一种情况是obj是一个普通实例,找到就直接返回,找不到进行下一步。第二种情况是obj是一个类,依次在obj和它的父类、祖先类的__dict__中查找,如果找到一个descriptor就返回descriptor的__get__方法的结果,否则直接返回attr。如果没有找到,进行下一步。

      4.在obj.__class__.__dict__中查找,如果找到了一个descriptor(插一句:这里的descriptor一定是non-data descriptor,如果它是data descriptor,第二步就找到它了)descriptor的__get__方法的结果。如果找到一个普通属性,直接返回属性值。如果没找到,进行下一步。

      5.很不幸,Python终于受不了。在这一步,它raise AttributeError



      首先,Python判断name属性是否是个自动产生的属性,如果是自动产生的属性,就按特别的方法找到这个属性,当然,这里的name不是自动产生的属性,而是我们自己定义的,Python于是到t的__dict__中寻找。还是没找到。

      接着,Python找到了t所属的类T,搜索T.__dict__,期望找到name,很幸运,直接找到了,于是返回name的值:字符串‘name’。如果在T.__dict__中还没有找到,Python会接着到T的父类(如果T有父类的话)的__dict__中继续查找。

      总结:


      只要记住类中的成员变量是对象的时候,对象又是个修饰器的时候。那么对成员变量的赋值实际是调用他的get和set方法就O了。参数很简单。


      标签:__,set,obj,attr,get,descriptor,dict,class
      From: https://blog.51cto.com/u_16081664/6224139

      相关文章

      • 团队绩效考核
        团队简介:希望能做队共有3人,根据一一一模式组成,一个设计查询调用接口和设计程序框架,一个负责博客以及界面美工等,一个测试人员并及时找出BUG并商讨修改。我们这个团队努力按照老师要求,去完成10天小冲刺完成设计。1、 理想目标加强团队凝聚力,避免诱发矛盾鼓励能者多劳,略不能者......
      • EAS_在ListUIETCX.java中校验是否选中行
        /***对内背书*/publicvoidactionEndorseIn_actionPerformed(ActionEvente)/**/throwsException/**/{checkSelected();ArrayListidList=getSelectedIdValues();ReceivableBillCollec......
      • java学习之七:使用匿名类直接new接口
        ......
      • Windows CMD常用命令
        1.dir查看目录dir 2.route操作网络路由表双网卡下添加静态路由示例:(1)删除默认路由(0.0.0.0是指所有地址)routedelete0.0.0.0(2)添加内网静态路由routeadd10.37.0.0mask255.255.0.010.37.132.129-p注:它表示访问10.37.0.0网段的所有数据都要经过网关10.37.132......
      • sqlplus工具迁移数据经验
            需要紧急把测试服务器数据迁移到pc机用于演示,但pc机无网络又没有客户端工具,只能使用sqlplus,且导出得文件又是sql文件,失去工具支持导入共过程遇到很多问题,如乱码问题,sql文件中特殊字符,日志存放和查看,导出文件不能写入,安装过程目录写入权限等大小问题。希望通过这篇博客把......
      • Java学习之六:“this”和“类名.this”以及“类名.class”的区分和详解
        目录 目录引言:1.Class类介绍:1.1Class类简介:1.2得到类对象的三个方法:1.3Class类的常用方法:2.this关键词:3.类名.this:4.总结 引言:对于以上三个语法结构的区分,需要先理解Class类所有对象的类以及调用了静态方法的类都需要在对象创建之前在JVM虚拟机中加载,加载内容被称为“类......
      • chatgpt代写---王者荣耀甄姬攻略
        在王者荣耀中,甄姬是一位高贵优雅的法师英雄,而要想完美掌握她的操作技巧,我们需要其四个经典的技能:重生、御风之力、清波锁月和紫电穹苍。首先,让我们来看看甄姬的被动技能——“重生”,这个技能非常厉害,一旦触发就可以像不死鸟一样再次升起。利用这个技能,我们可以在团队战中扮演一个......
      • 模糊查询+首字母查询组合框
         <spanclass="label">站名称:</span><divid="selectMain"><inputid="myInputOne"name="Q_bdzmc_S"onkeyup="InputAndSelect.ke......
      • linux之dlopen、dlsym和dlclose使用和举例
         之前用过这三个函数一直没时间整理一下。今天抽时间整理一下。1、函数简介dlopen基本定义功能:打开一个动态链接库 包含头文件: #include<dlfcn.h> 函数定义: void*dlopen(constchar*pathname,intmode); 函数描述: 在dlopen的()函数以指......
      • 线程机制与事件机制
        进程与线程进程程序的一次执行--占有一片独有的内存空间可通过windows任务管理器查看进程线程进程内的一个独立执行单元是程序执行的一个完整流程是CPU的最小调度单位相关知识应用程序必须运行在某个进程的某个线程上一个进程中至少有一个运行的线程--主线程--进......