Python学习之路(41)——Python的"__getattr__"和"__getattribute__"

__getattr__

__getattr__在当前主流的Python版本中都可用,重载__getattr__方法对类机器实例未定义的属性有效。即,如果访问的属性(包括类属性和实例属性)存在,就不会调用__getattr__方法。

Python官方文档描述:

>>> class ClassA:
	x = 'a'
	def __init__(self):
		self.y = 'b'
	def __getattr__(self, item):
		return '__getattr__'

	
>>> a= ClassA()
>>> print(a.x)
a
>>> print(a.y)
b
>>> print(a.z)
__getattr__

  

__getattribute__

__getattribute__仅在新式类中可用,重载__getattribute__方法对类实例的每个属性访问都有效。

Python官方文档描述:

当同时定义__getattribute__和__getattr__时,__getattr__方法不会被调用,除非显式调用__getattr__方法或引发AttributeError异常。

>>> class ClassA:
	x = 'a'
	def __init__(self):
		self.y = 'b'
	def __getattr__(self, item):
		return '__getattr__'
	def __getattribute__(self, item):
		return '__getattribute__'

	
>>> a = ClassA()
>>> a.x
'__getattribute__'
>>> a.y
'__getattribute__'
>>> a.z
'__getattribute__'

由于__getattr__只针对未定义属性的调用,所以它可以在自己的代码中自由地获取其他属性,而__getattribute__针对所有的属性运行,因此要时分注意避免在访问其他属性时,再次调用自身而引起的递归循环。(比如,当在__getattribute__代码块中,再次执行属性的获取操作,会再次触发__getattribute__方法的调用,代码将会陷入无限递归,直到达到Python递归深度限制,抛出ecursionError异常。重载__setattr__等方法也会有这个问题。)

>>> class ClassA:
	x = 'a'
	def __getattr__(self, item):
		print('__getattr__')
		return self.item

	
>>> a = ClassA()
>>> a.x
'a'
>>> 
>>> class ClassB:
	x = 'b'
	def __getattribute__(self, item):
		print('__getattribute__')
		return self.item

	
>>> b = ClassB()
>>> b.x
__getattribute__
__getattribute__
__getattribute__
....
....
....
  File "<pyshell#135>", line 4, in __getattribute__
    print('__getattribute__')
  File "C:\Python35\lib\idlelib\PyShell.py", line 1344, in write
    return self.shell.write(s, self.tags)
RecursionError: maximum recursion depth exceeded while calling a Python object
>>>

同样,也无法通过从__dict__取值的方法来避免无限递归问题:

>>> class ClassC:
	x = 'c'
	def __getattribute__(self, item):
		return self.__dict__[name]

	
>>> c = ClassC()
>>> c.x
Traceback (most recent call last):
  File "<pyshell#152>", line 1, in <module>
    c.x
  File "<pyshell#150>", line 4, in __getattribute__
    return self.__dict__[name]
  File "<pyshell#150>", line 4, in __getattribute__
    return self.__dict__[name]
  File "<pyshell#150>", line 4, in __getattribute__
...
...
...
  File "<pyshell#150>", line 4, in __getattribute__
    return self.__dict__[name]
RecursionError: maximum recursion depth exceeded
>>> 

  

为了避免无限递归,应该把获取属性的方法指向一个更高的超类:

>>> class ClassD:
	x = 'd'
	def __getattribute__(self, item):
		print('__getattribute__')
		return object.__getattribute__(self, item)

	
>>> d = ClassD()
>>> d.x
__getattribute__
'd'

  

小结:

__getattr__(self, item)    定义当用户试图获取一个不存在的属性时的行为

__getattribute__(self, item)  定义当该类的属性被访问时的行为

__setattr__(self, item)    定义当一个属性被设置时的行为

__delattr__(self, item)    定义当一个属性被删除时的行为

>>> class ClassE:
	def __getattr__(self, item):
		print('__getattr__')
	def __getattribute__(self, item):
		print('__getattribute__')
		return object.__getattribute__(self, item)
	def __setattr__(self, item, value):
		print('__setattr__')
		object.__setattr__(self, item, value)
	def __delattr__(self, item):
		print('__delattr__')
		object.__delattr__(self, item)

		
>>> e = ClassE()
>>> e.x
__getattribute__
__getattr__
>>> e.x = 'e'
__setattr__
>>> e.x
__getattribute__
'e'
>>> del e.x
__delattr__

  

再看一个计算矩形面积的类:

>>> class Rectangle:
	def __init__(self, width = 0, height = 0):
		self.width = width
		self.height = height
	def __setattr__(self, name, value):
		if name == 'square':
			self.width = value
			self.height = value
		else:
			object.__setattr__(self, name, value)
	def getArea(self):
		return self.width * self.height

	
>>> a1 = Rectangle()
>>> a1.getArea()
0
>>> a2 = Rectangle(10, 20)
>>> a2.width
10
>>> a2.height
20
>>> a2.getArea()
200
>>> a3 = Rectangle(10, 20)
>>> a3.square = 30
>>> a3.width
30
>>> a3.height
30
>>> a3.getArea()
900

  

posted on 2018-03-22 15:35  nicolas_Z  阅读(432)  评论(0)    收藏  举报

导航