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

‘‘‘

  

时间: 2024-10-12 02:54:13

python中的 descriptor的相关文章

python中基于descriptor的一些概念(上)

@python中基于descriptor的一些概念(上) python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(new-style class) 2.3.1 __init__方法 2.3.2 __new__静态方法 2.4. 新式类的实例 2.4.1 Property 2.4.2 __slots__属性 2.4.3 __getattribute__方法

python中基于descriptor的一些概念(下)

@python中基于descriptor的一些概念(下) 3. Descriptor介绍 3.1 Descriptor代码示例 3.2 定义 3.3 Descriptor Protocol(协议) 3.4 Descriptor调用方法 4. 基于Descriptor实现的功能 4.1 property 4.2 函数和方法,绑定与非绑定 4.3 super 5. 结尾 3. Descriptor介绍 3.1 Descriptor代码示例 class RevealAccess(object):   

python 中关于descriptor的一些知识问题

这个问题从早上日常扫segmentfault上问题开始 有个问题是 class C(object): @classmethod def m(): pass m()是类方法,调用代码如下: C.m() 但我想当成属性的方式调用,像这样: C.m 请问该怎么弄呢? 请最好提供个简单的例子, 多谢! 这里我开始误会了他的意思,以为他是想直接使用C().m调用这个方法,如果是这样,直接将装饰器@classmathod改成@property就可以达到效果了. 但是这里他想要达到的效果是C.m 也就是说在不

Python中的Descriptor

Python中的描述符 描述符的定义: 通常情况下,我们可以认为"假设对象的某个属性被绑定了(__get__, __set__, __delete__)这三个方法中的任意一个方法",那么我们称该属性为"描述符" class Foo(object): def init(self, name, age): self.name = name self.age = age foo = Foo("pizza", 18) 我们不能称 foo.name, fo

Python中常见的文件对象内建函数

文件对象内建方法列表 文件对象的方法 操作 file.close() 关闭文件 file.fileno() 返回文件的描述符(file descriptor,FD,整数值) file.flush() 刷新文件的内部缓冲区 file.isatty() 判断file是否是一个类设tty备 file.next() 返回文件的下一行,或在没有其它行时引发StopIteration异常 file.read(size=-1) 从文件读取size个字节,当未给定size或给定负值时读取剩余的所有字节,然后作为

Python中的socket如何使用?

本文和大家分享的主要是python 中socket相关内容,一起来看看吧,希望对大家 学习python有所帮助. 一. socket模块 socket ,俗称套接字,其实就是一个 ip 地址和端口的组合.类似于这样的形式 (ip,  port), 其中 ip 代表的是某个主机, port 代表的是某个应用,我们可以通过 socket 和另外的一台主机进行通信. 关于socket 源码的解析在 tarnado 系列文章中,正在写中..... 1. 通信的方式 tcp 通信 udp 通信 基于uni

python描述符descriptor(二)

python内置的描述符 python有些内置的描述符对象,property.staticmethod.classmethod,python实现如下: class Property(object): def __init__(self,getf,setf,delf,doc): self.getf=getf self.setf=setf self.delf=delf self.doc=doc def __set__(self,instance,own=None): if instance is N

python2.7高级编程 笔记二(Python中的描述符)

Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些"中级"的语言特性有着完善的文档,并且易于学习. 但是这里有个例外,那就是描述符.至少对于我来说,描述符是Python语言核心中困扰我时间最长的一个特性.这里有几点原因如下: 有关描述符的官方文档相当难懂,而且没有包含优秀的示例告诉你为什么需要编写描述符(我得为Raymond Hettinger辩护一

python中os.system()的返回值

最近遇到os.system()执行系统命令的情况,上网搜集了一下资料,整理如下,以备不时之需,同时也希望能帮到某些人. 一.python中的 os.system(cmd)的返回值与linux命令返回值(具体参见本文附加内容)的关系 大家都习惯用os.systemv()函数执行linux命令,该函数的返回值十进制数(分别对应一个16位的二进制数).该函数的返回值与 linux命令返回值两者的转换关系为:该函数的返回值(十进制)转化成16二进制数,截取其高八位(如果低位数是0的情况下,有关操作系统的