学好和用好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

python中的 descriptor相关推荐

  1. python 描述器 详解_深入解析Python中的descriptor描述器的作用及用法

    一般来说,一个描述器是一个有"绑定行为"的对象属性(object attribute),它的访问控制被描述器协议方法重写.这些方法是 __get__(), __set__(), 和 ...

  2. python中的新式类与旧式类的一些基于descriptor的概念(上)

    python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(n ...

  3. python中@property以及描述符descriptor详解

    python一直以代码简洁优雅而著称,这篇文章介绍的小技巧,就是如何优雅地对一个类的属性进行赋值和取值.不过不仅仅如此,本文章还为类属性的查找顺序,以及装饰器在类方法的使用打下了基础. 文章目录 待解 ...

  4. Learning by doing 系列文章(之一)如何在 Python 中使用 epoll ?

    epoll 简介 参见本博前一文<epoll使用详解> Epoll Within Python Python 在 2.6 版中引入了用于处理Linux epoll系统调用的API,本文简单 ...

  5. 通过命令行在Python中测试以太坊RPC客户端

    2019独角兽企业重金招聘Python工程师标准>>> 在这个笔记中,我将使用Python命令行测试以太坊的RPC客户端,准备好狂敲键盘吧.过程中有关JSON RPC的更多信息,可以 ...

  6. python中append函数合并列表且列表内数字从高到低_35个高级Python知识点总结

    No.1 一切皆对象 众所周知,Java中强调"一切皆对象",但是Python中的面向对象比Java更加彻底,因为Python中的类(class)也是对象,函数(function) ...

  7. 在Python中对子进程进行非阻塞读取

    我正在使用子流程模块来启动子流程并连接到其输出流(stdout). 我希望能够在其stdout上执行非阻塞读取. 有没有一种方法可以使.readline成为非阻塞状态,或者在调用.readline之前 ...

  8. python中使用 protocol buffer(Protobuf)

    项目中引入proto的依赖 [两种方法]: 方法1. 官网下载对应的语言包,https://github.com/protocolbuffers/protobuf/releases 这里选择pytho ...

  9. python中类方法与实例方法的区别-Python中的对象,方法,类,实例,函数用法分析...

    本文实例分析了Python中的对象,方法,类,实例,函数用法.分享给大家供大家参考.具体分析如下: Python是一个完全面向对象的语言.不仅实例是对象,类,函数,方法也都是对象. class Foo ...

最新文章

  1. 业界丨2018年能干大事儿的5家人工智能初创公司
  2. J2SE理解之一:声明和访问控制
  3. java 如何判定消息已在队列_【05期】消息队列中,如何保证消息的顺序性?
  4. python字符串结合操作符的使用
  5. 【英语学习】【Level 07】U02 Live Work L2 A place to call my home
  6. Uncaught TypeError: this.canvas.getContext is not a function
  7. recvfrom函数 非阻塞_那些年让你迷惑的阻塞、非阻塞、异步、同步
  8. 怎么将linux的动态IP设置成静态IP
  9. 嵌入式 Linux 4.0,嵌入式多媒体中心 OpenELEC 4.0.4
  10. 凸优化第九章无约束优化 9.1无约束优化问题
  11. ThinkPHP扩展,实现Redis的CURD操作。
  12. windows 中hosts文件
  13. 有关于阿里云的历史-阿里云这群疯子
  14. 双舵轮AGV轨迹跟踪Pure Pursuit算法模型分析、python代码实现
  15. 阿里云邮箱怎么样,阿里云企业邮箱优势
  16. 【论文笔记】Contextual Diversity for Active Learning(ECCV2020)
  17. pvm虚拟机基本原理
  18. 是非人生 — 一个菜鸟程序员的5年职场路 第14节
  19. 20189230杨 2018-2019-2 《密码与安全新技术专题》第3周作业
  20. Clojure CLR 入门

热门文章

  1. bootstrap在ie8下,兼容媒体查询
  2. JavaScript学习总结(六)——JavaScript判断数据类型总结
  3. 刚刚出炉的Asp.net网站部署视频教程
  4. dct变换的主要优点有哪些_发泡保温材料包括哪些成分?主要优点是什么?有没有发展前景?...
  5. 百度的html代码是什么,百度网页源代码是什么?
  6. Lintcode--2(56)--两数之和
  7. python Django基本介绍
  8. 普通java程序怎样用cron_java – Spring cron vs普通cron?
  9. java中三个基本框架_对于Java基础者应该如何理解Java中的三大框架!
  10. 用python验证猜想之类的例子_python练手好例子:验证哥德巴赫猜想