python中的 descriptor

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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访问属性:

  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__
‘‘‘

 


三、通过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__=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

‘‘‘

  

以上是关于python中的 descriptor的主要内容,如果未能解决你的问题,请参考以下文章

Python中的Descriptor

python中的 descriptor

python类:描述器Descriptors和元类MetaClasses

python Descriptor (描述符)

python学习笔记-类的descriptor

Python描述符(descriptor)解密(转)