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 2 class A(object):
pass class 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 2 class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass a=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 2 class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass def __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 2 class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass def __getattribute__(self,name):
print("B.__getattribute__ name=",name)
return type(self).datadescriptor a=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 2 class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass def __getattribute__(self,name):
print("B.__getattribute__ name=",name)
raise AttributeError()
#return None def __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 2 class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass #def __getattribute__(self,name):
# print("B.__getattribute__ name=",name)
# raise AttributeError()
#return None def __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.values class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass a=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.values
def __set__(self,instance,value):
print("DataDescriptor.__set__ ",instance,value) class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass a=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.values
def __set__(self,instance,value):
print("DataDescriptor.__set__ ",instance,value) class A(object):
datadescriptor=DataDescriptor() class B(A):
def __init__(self):
pass def __setattr__(self,key,value):
print("B.__setattr__ ",key,value)
self.__dict__[key]=value a=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访问属性的规则为:
- 如果MetaClass中有__getattribute__,则直接返回该__getattribute__的结果。
- 如果attr是个Descriptor,则直接返回Descriptor的__get__的结果。
- 如果attr是class.dict中的属性,则直接返回attr的值
- 如果类中没有attr,且MetaClass中定义了__getattr__,则调用MetaClass中的__getattr__
- 如果类中没有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__=Metaclass print classB.datadescriptor print 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]这种形式,方便很多。
python中的 descriptor的更多相关文章
- python中基于descriptor的一些概念
python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(n ...
- python 中关于descriptor的一些知识问题
这个问题从早上日常扫segmentfault上问题开始 有个问题是 class C(object): @classmethod def m(): pass m()是类方法,调用代码如下: C.m() ...
- python中基于descriptor的一些概念(上)
@python中基于descriptor的一些概念(上) python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2 ...
- python中基于descriptor的一些概念(下)
@python中基于descriptor的一些概念(下) 3. Descriptor介绍 3.1 Descriptor代码示例 3.2 定义 3.3 Descriptor Protocol(协议) 3 ...
- python中descriptor的应用
[python中descriptor的应用] 1.classmethod. 1)classmethod的应用. 2)classmethod原理. 2.staticmethod. 1)staticmet ...
- python2.7高级编程 笔记二(Python中的描述符)
Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些" ...
- Python中实现异步并发查询数据库
这周又填了一个以前挖下的坑. 这个博客系统使用Psycopy库实现与PostgreSQL数据库的通信.前期,只是泛泛地了解了一下SQL语言,然后就胡乱拼凑出这么一个简易博客系统. 10月份找到工作以后 ...
- Python中的属性管理
Python管 理属性的方法一般有三种:操作符重载(即,__getattr__.__setattr__.__delattr__和 __getattribute__,有点类似于C++中的重载操作符).p ...
- Python 描述符(descriptor) 杂记
转自:https://blog.tonyseek.com/post/notes-about-python-descriptor/ Python 引入的“描述符”(descriptor)语法特性真的很黄 ...
随机推荐
- mongostat用法
mongostat是mongoDB自带的工具,用于检测mongodb的运行状态. mongostat用法 Test:Test/node-131 / # mongostat --help Usage: ...
- SQL索引添加
EXPLAIN select * from view_agzsaycommont where id >0
- 下载VM12 虚拟机和安装kali
为什么现在才写这个-- 因为我在学校啊,学校的电脑还没有kali.好了我们开始. http://www.vmware.com/products/player/playerpro-evaluation ...
- git基本配置
用户信息 你个人的用户名称和电子邮件地址,用户名可随意修改,git 用于记录是谁提交了更新,以及更新人的联系方式. $ git config --global user.name "Donl ...
- 对IOC和DI以及AOP的理解
为了理解Spring的IoC与DI从网上查了很多资料,作为初学者,下面的描述应该是最详细,最易理解的方式了. 首先想说说IoC(Inversion of Control,控制倒转).这是spring的 ...
- jQuery之元素的遍历与元素的过滤
jQuery遍历之向下遍历 jQuery遍历之向上遍历 jQuery遍历之同级遍历 jQuery遍历之过滤
- c++ 解包tar
首先我们来看tar文件组成 tar中的数据都是以512字节为单位:tar由三部分组成 “头部+内容+尾部”,其中头部是512字节的头部结构,内容是存放一个文件内容的地方,最后尾部是一个512字节的全零 ...
- eclipse安装svn
- Nop源码分析三
程序的初始化工作和Ioc工作已经做完,nop默认引擎已经初始化. 下面在回到global文件的启动方法Application_Start()中, 1,继续分析下面的代码: var dependency ...
- Wireshark工控协议
Wireshark是一个强大开源流量与协议分析工具,除了传统网络协议解码外,还支持众多主流和标准工控协议的分析与解码. 序号 协议类型 源码下载 简介 1 Siemens S7 https://git ...