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
浙公网安备 33010602011771号