python中的 descriptor

学好和用好python, descriptor是必须跨越过去的一个点,现在虽然Python书籍花样百出,但是似乎都是在介绍一些Python库而已,对Python语言本身的关注很少,或者即使关注了,但是能够介绍把 dscriptor介绍清楚的,是很少的,到目前,我自己还没有见到过。

一个attr能被称为descriptor,除了需要定义 descriptor protocol 规定的方法外,这个attr必须是属于某个class的,不能是属于某个instance

一、Python中的descriptor

  在一个Python class 中重写下面任何一个方法都称为descriptor

    1.__get__(self,obj,type=None)---->value

    2.__set__(self,obj,value)---->None

    3.__delete__(self,obj)---->None

  descriptor细分:

     1.Data descriptor :      只是重写__get__,__set__的class

     2.None Data descriptor:    只是重写了__get__的class

     3.read-only Data descriptor     同时定义了__get__,__set__,但是这个__set__只是raise AttributeError

  Data descriptor和None Data descriptor 的区别:相对于 instance 字典的优先级。 

           若实例字典中有与描述器同名的属性,若描述器为资料描述器,则优先访问资料描述器;若描述器为非资料描述器,

           则优先使用字典中的属性。这条规则在实际应用中的例子:如果实例中有方法和属性重名时,Python会优先使用实例字典中的属性,

           因为实例函数的实现是个非资料描述器。

 


二、通过instance访问属性:

  1.获取attr

  instance.a

__getattribute__,__getattr__,__get__和__dict__都与属性访问有关,它们的优先级:

1.当类中( type(instance) )定义了__getattribute__方法时,无条件的调用__getattribute__.所以在__getattribute__方法中,不能出现self.__attr__这种调用,它会引起无限制递归

2.如果访问的attr存在,并且这个attr是属于 type(instance)的或者属于type(instace) 的某个父类(是super class 不是metaclass)的,并且这个attr是一个descriptor那么,此时会转而继续调用都相应 class.__get__。 简而言之:

  2.1 这个attr是个Descriptor,是调用这个属性的__get__

2.2这个attr不是一个Descriptor,就调用__dict__[attr]

3.如果类中没有定义该属性,则调用__getattr__

4.否则,抛出异常AttributeError

 

  • 实验一 : 在self.__dict__可以获得某个遵守了descriptor的attr,这个attr不是一个descriptor,所以不遵守descriptor规则
class DataDescriptor(object):def __get__(self,obj,owner):print("datadescriptor.__get__ ",self,obj,owner)return 2class A(object):passclass B(A):def __init__(self):self.datadescriptor=DataDescriptor()a=B()
print a.datadescriptor
#输出<__main__.DataDescriptor object at 0x00BD8DB0>
  • 实验二:在class.__dict__中得到attr,并且这个attr是一个descriptor
class DataDescriptor(object):def __get__(self,obj,owner):print("datadescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passa=B()
print a.datadescriptor
'''
输出 ('datadescriptor.__get__ ', <__main__.DataDescriptor object at 0x00BD8CF0>, <__main__.B object at 0x00BD8D50>,<class '__main__.B'>)
'''
  • 实验三:__getattribute__返回非descriptor

 

class DataDescriptor(object):def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __getattribute__(self,name):print("B.__getattribute__  name=",name)return "abc"a=B()
print a.datadescriptor
'''
输出:('B.__getattribute__  name=', 'datadescriptor')
abc'''

 

  • 实验四: __getattribute__返回descriptor,遵守descriptor规则
	def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __getattribute__(self,name):print("B.__getattribute__  name=",name)return type(self).datadescriptora=B()
print a.datadescriptor'''
输出:
('B.__getattribute__  name=', 'datadescriptor')
('DataDescriptor.__get__ ', <__main__.DataDescriptor object at 0x00BD8CB0>, None, <class '__main__.B'>)
2
'''
  • 实验五,在找不到attr的情况下

这种情况比较特殊,在__getattribute__中return None 或者 没有return 语句,都不会调用,只有 在__getattribute__中 raise AttributeError(),才会调用 __getattr__,如果没有定义__getattribute__ ,在找不到attribute的情况下,VM默认是会raise AttributeError()的.

 代码1

class DataDescriptor(object):def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __getattribute__(self,name):print("B.__getattribute__  name=",name)raise AttributeError()#return Nonedef __getattr__(self,name):print("B.__getattr__ name=",name)return "Not Found"a=B()
print a.datadescriptor
'''
定义了__getattribute__,但是 raise AttributeError了,所以会转而继续调用到__getattr__,没有没有 raise AttributeError,无论__getattribute__中做了什么,都不会继续调用__getattr__
'''

  代码2

class DataDescriptor(object):def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):pass#def __getattribute__(self,name):#	print("B.__getattribute__  name=",name)#	raise AttributeError()#return Nonedef __getattr__(self,name):print("B.__getattr__ name=",name)return "Not Found"a=B()
print a.zz
'''
找不到zz 这个attr,vm默认会 raise AttributeError,自动转而调用__getattr__
'''

  2.设置instance.attr 

  设置instance.attr=value时,涉及到三个方法,分别为__setattr__、__set__和__dict__[attr]=val,没有__setattribute__

    调用的优先级为:

  1.如果type(instance) 中定义了__setattr__方法,就直接调用这个方法。

  2.如果这个attr是个descriptor,那会分情况:

    2.1,如果是个data descriptor(定义了 __set__方法),那么会调用 data descriptor的__set__方法

    2.2,如果是个None data descriptor(没有定义__set__方法),那么会是instance.__dict__[attr]=value

  3.如果attr不是descriptor,会直接instance.__dict__[attr]=value

   实验一:None data descriptor时的设置

  

# -*- coding:utf-8 -*- class DataDescriptor(object):def __init__(self):self.values={};def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return self.valuesclass A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passa=B()
a.datadescriptor=999
print a.__dict__'''
输出:
{'datadescriptor': 999}
'''

  实验二:Data descriptor时的set attr

# -*- coding:utf-8 -*- class DataDescriptor(object):def __init__(self):self.values={};def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return self.valuesdef __set__(self,instance,value):print("DataDescriptor.__set__ ",instance,value)class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passa=B()
a.datadescriptor=999
print a.__dict__'''
输出:
('DataDescriptor.__set__ ', <__main__.B object at 0x00BD8E30>, 999)
{}
'''

  可以看出在data descriptor时,设置相应的data descriptor attribute时,没有影响到instance.__dict__

  实验三:type(instance)有定义__setattr__方法时:

  

# -*- coding:utf-8 -*- class DataDescriptor(object):def __init__(self):self.values={};def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return self.valuesdef __set__(self,instance,value):print("DataDescriptor.__set__ ",instance,value)class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __setattr__(self,key,value):print("B.__setattr__ ",key,value)self.__dict__[key]=valuea=B()
a.datadescriptor=999
print a.__dict__'''
输出:
('B.__setattr__ ', 'datadescriptor', 999)
{'datadescriptor': 999}
'''

  当type(instance)有定义__setattr__方法时,那么是否是 descriptor就无关紧要了,都会调用这个__setattr__

  

  2,删除instance.attr 

  删除instance.attr和设置instacne.attr的情况非常类似,涉及到三个方法或情况:__delattr__或__delete__ , 删除 instance.__dict__

  优先级也是和设置instance.attr一样的:

    1.如果type(instance)定义了__delattr__,那么直接调用,无论这个attr是否为descriptor

    2.如果没有定义__delattr__,并且是descriptor

        2.1,如果这个descriptor 定义了 __delete__,那么调用__delete__方法

        2.2如果这个descriptor 没有定义__delete__,那么raise AttributeError

    3.del intance.__dict__[attr]

 


三、通过class访问属性

通过class object来获取attr在概念上其实和通过instance来获取属性是一样的,instance 的class 是某个class object,而 class object 的class 应该是这个class的 metaclass

当在class object 的dict中找不到attr时,会转而向 class 的metaclass的dict中去寻找.

通过ClassA.attr访问属性的规则为:

  1. 如果MetaClass中有__getattribute__,则直接返回该__getattribute__的结果。
  2. 如果attr是个Descriptor,则直接返回Descriptor的__get__的结果。
  3. 如果attr是class.dict中的属性,则直接返回attr的值
  4. 如果类中没有attr,且MetaClass中定义了__getattr__,则调用MetaClass中的__getattr__
  5. 如果类中没有attr,且MetaClass中没有定义__getattr__,则抛出异常AttributeError
  •  实验
class Metaclass(type):datadescriptor=DataDescriptor()def __new__(metaclz,name,bases,attrs):print("create new class ",metaclz,name)return type.__new__(metaclz, name, bases, attrs)def __getattr__(self,name):print("Metaclass.__getattr__ name:",name)#def __getattribute__(self,name):#	print("Metaclass.__getattribute__ name:",name)#	return name+'a'class classB(object):__metaclass__=Metaclassprint classB.datadescriptorprint classB.ss
'''
输出('create new class ', <class '__main__.Metaclass'>, 'classB')
('DataDescriptor.__get__ ', <__main__.DataDescriptor object at 0x00BD8EF0>, <class '__main__.classB'>, <class
'__main__.Metaclass'>)
2
('Metaclass.__getattr__ name:', 'ss')
None'''

 

其实可以发现descriptor的主要作用是起到了保护作用,当某种类型的变量被访问的时候,在给一次程序员一个控制的机会。

另外__getattr__也有类似的作用,__getattr__的用法有很多,典型的是在 web程序中,经常要有request.attr 、request[attr]这种操作,那么这个时候,把本需要用函数(类似 request.get(name) )来获取某些状态变量的操作,转成 request.attr 、request[attr]这种形式,方便很多。

 

转载于:https://www.cnblogs.com/hi0xcc/p/5586601.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374515.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Heroku运行Java

如果您是像我这样的Java迷&#xff0c;那么您有个好消息值得振奋。 Heroku现在运行Java&#xff01; 嗯&#xff0c;与其他流行的“ Web”语言&#xff08;如PHP / RoR&#xff09;不同&#xff0c;Java具有在Web服务器中进行部署和维护的麻烦。 一直以来&#xff0c;只有企业才…

配置了坐标还是找不到serv_为什么老人家总是这疼那疼,还找不到原因?是矫情还是另有原因...

“哎呀&#xff0c;怎么回事&#xff0c;腰痛腿痛的&#xff0c;痛的我一晚上都没怎么睡觉&#xff0c;怎么回事昨天也没干嘛啊&#xff01;”对于这一句话&#xff0c;相信很多朋友都不是很陌生。这句话是来自于一位网友的留言&#xff0c;而说这话的真是她的妈妈。这也是很多…

Nginx负载均衡和LVS负载均衡的比较分析

lvs和nginx都可以用作多机负载的方案&#xff0c;它们各有优缺&#xff0c;在生产环境中需要好好分析实际情况并加以利用。 首先提醒&#xff0c;做技术切不可人云亦云&#xff0c;我云即你云&#xff1b;同时也不可太趋向保守&#xff0c;过于相信旧有方式而等别人来帮你做垫被…

QuartZ.net 常用配置说明

配置文件说明app.config中的quartz部分<quartz><!-- configure Thread Pool--><addkey"quartz.threadPool.type"value"Quartz.Simpl.SimpleThreadPool,Quartz" /><addkey"quartz.threadPool.threadCount"value"10&quo…

qopenglwidget 透明_廊坊透明真空袋用途-祺泰包装

功能方面&#xff1a;平面真空袋抽真空后易形成不平整&#xff0c;不均匀的现象。目前&#xff0c;真空包装主要用于食品的包装&#xff0c;如肉类、谷类加工食品以及易氧化变质的食品&#xff0c;也可用于机械零件、仪器和羽绒制品、毛制品等蓬松制品的包装。在超shi中&#x…

使用Spring Data Neo4j进行领域建模

大家好&#xff0c;威利在这里。 上次我告诉您&#xff0c;我正在使用Neo4j和Spring Data Neo4j构建Skybase CMDB&#xff0c;我很高兴收到很多对此的积极反馈。 我展示了一些代码&#xff0c;但没有那么多。 在本文中&#xff0c;我将向您展示如何使用Spring Data Neo4j在Skyb…

mysql 阿里内核人员

丁奇 http://dinglin.javaeye.com/鸣嵩 曹伟-鸣嵩 (新浪微博)彭立勋 http://www.penglixun.com/皓庭 http://wqtn22.iteye.com/项仲 http://blog.csdn.net/wudongxu剑川 http://gaoyusong.com/武藏 http://ybbct.iteye.com/祁奚 http://i.mtime.com/844165/褚霸 http://blog.y…

linux基本命令学习

1. 执行文件&#xff1a; ./文件 &#xff1a;执行该文件 sh startup.sh 执行该文件 2. Vi编辑 vi命令&#xff1a; :w 保存文件但不退出 :w file 将修改另外保存到file中&#xff0c;不退出 :w! 强制保存&#xff0c;不推出 :wq 保存文件并退出 :wq! 强制保存文件&#xff0…

506. 相对名次

方法一 复制数组排序返回对应下标 class Solution {public String[] findRelativeRanks(int[] score) {int n score.length;int[] newScore Arrays.copyOf(score,n);Arrays.sort(newScore);String[] res new String[n];for(int i 0;i<n;i){int rank Arrays.binarySear…

Java压缩

在最近的项目中&#xff0c;我们不得不做一些我个人从未真正看过的事情。 压缩。 我们需要拍几个文件和图像&#xff0c;将它们压缩并提供给FTP使用&#xff0c;是的&#xff0c;总有一天&#xff0c;感觉确实回到了90年代。 除了过去的FTP之行外&#xff0c;它还是一个很好的机…

苏州面对公司发布

假设您对我们这种创业型公司和我们的发展方向感兴趣的话&#xff0c;我们希望通过以下10个问答进一步添加两方的了解。我们希望看到的是您经过深思熟虑的、对公司和自己的前途负责任的谨慎回答。而不是应付公差式的轻描淡写&#xff08;我们会依据您回答质量的高低决定是否邀请…

linux多线程_Java+Linux,深入内核源码讲解多线程之进程

之前写了两篇文章&#xff0c;都是针对Linux这个系统的&#xff0c;为什么?我为什么这么喜欢写这个系统的知识&#xff0c;可能就是为了今天的内容多线程系列&#xff0c;现在多线程不是一个面试重点 啊&#xff0c;那如果你能深入系统内核回答这个知识点&#xff0c;面试官会…

594. 最长和谐子序列

和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 现在&#xff0c;给你一个整数数组 nums &#xff0c;请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的子序列是一个由数组派生出来的序列&#xff0c;它可以通过删除一些元素或不删除元素、…

解决git clone报错SSL certificate problem

Git新手一枚&#xff0c;今天进行git clone操作时发生如下问题&#xff1a;提示无效的链接error: SSL certificate problem: Invalid certificate chain while accessing https://githib.com/...XXXX.git fatal: HTTP request failed解决方法也很简单&#xff0c;一条命令就搞定…

使用内存映射文件获取巨大的矩阵

总览 矩阵可能真的很大&#xff0c;有时甚至比一个数组中可以容纳的更大。 您可以通过具有多个数组来扩展最大大小&#xff0c;但这会使堆大小确实很大且效率低下。 一种替代方法是在内存映射文件上使用包装器。 内存映射文件的优点是它们对堆的影响很小&#xff0c;并且可以由…

ipad连接电脑_这些应用让iPad生产力分分钟UP

IT时报见习记者 钱奕昀用iPad办公这件事&#xff0c;多年前网友就在讨论&#xff0c;最常见的还是那句“买前生产力&#xff0c;买后爱奇艺”。很长一段时间里&#xff0c;它的生产力属性都是弱于娱乐属性的。其实&#xff0c;作为PC端和移动端的形态中和&#xff0c;iPad可以…

Mac OSX 快捷键命令行

ctrlshift 快速放大dock的图标会暂时放大&#xff0c;而如果你开启了dock放大CommandOptionW 将所有窗口关闭CommandW 将当前窗口关闭(可以关闭Safari标签栏,很实用) CommandOptionM …

将JavaFX 2.0与Swing和SWT集成

JavaFX 2.0对JavaFX的改进之一是可以更轻松地与Swing和SWT进行互操作 。 一些在线资源记录了如何完成此操作。 其中包括将JavaFX集成到Swing应用程序和SWT Interop中 。 但是&#xff0c;在有效的类级Javadoc文档的一个很好的示例中&#xff0c;各自的JavaFX类javafx.embed.swi…

iOS-如何返回某个字符串的拼音助记码

我也是看了网上的一个示例代码后&#xff0c;在它的基础上进行的修改。因为项目上会用到&#xff0c;我相信很多人的项目上也会用到。所以实现后&#xff0c;也赶紧分享出来&#xff0c;希望后来人不需要花费时间了。 提示&#xff1a;这里用到了正则表达式&#xff0c;使用了一…

wifi rssi 计算 距离_WiFi和WLAN是一样的?真相在这里~别再傻傻分不清了

我们通常上网的时候会说连接WiFi如果注意到无线网络的名称就会发现手机的连接显示是WLAN别再将WiFI和WLAN搞混了&#xff01;二者的定义WLANWLAN的全称为 Wireless Local Area Networks,中文意思为无线局域网络&#xff0c;是一种数据传输系统。它是利用射频技术进行数据传输&a…