__getattr__和__getattribute__区别
1.__getattr__
和 __getattribute__
区别
1.
__getattr__
在访问对象的属性而属性不存在时触发
。它只会在属性不存在时调用,而对已存在的属性访问不会触发。2.
__getattribute__
在访问对象的任何属性时都会触发
。无论属性是否存在,每次属性访问都会经过__getattribute__
。
1.1使用__getattribute__
class A:
def __init__(self, x) -> None:
self.x = x
def __getattr__(self, name):
print(f'__getattr__ 【{name}】')
def __getattribute__(self, name: str):
print(f'__getattribute__ 【{name}】')
return 222
a = A(1)
print(a.x)
print(a.z)
__getattribute__ 【x】
222
__getattribute__ 【z】
222
1.2使用__getattr__
from builtins import AttributeError
class A:
def __init__(self, x) -> None:
self.x = x
def __getattr__(self, name):
print(f'__getattr__ 【{name}】')
def __getattribute__(self, name: str):
print(f'__getattribute__ 【{name}】')
AttributeError('not find')
a = A(1)
print(a.x)
print(a.z)
__getattribute__ 【x】
None
__getattribute__ 【z】
None
1.3__getattr__
递归问题
在
__getattr__
方法中,你是可以使用self.undefined_attr
的语法的。当你在__getattr__
中使用类似self.undefined_attr
的语法时,它实际上会触发对__getattribute__
的调用。这可能导致无限递归,因为在__getattribute__
中使用类似self.undefined_attr
的语法又会导致__getattr__
的调用。
class A:
def __init__(self, x) -> None:
self.x = x
def __getattr__(self, name):
print(f'__getattr__ 【{name}】')
# 如果访问不存在的信息,那么将会无线递归
return self.name
def __getattribute__(self, name: str):
print(f'__getattribute__ 【{name}】')
return super().__getattribute__(name)
a = A(1)
# 访问不存在的属性z
print(a.z)
__getattribute__ 【name】
__getattr__ 【name】
__getattribute__ 【name】
Traceback (most recent call last):
File "/Users/code/python_code/private.py", line 17, in <module>
print(a.z)
File "/Users/code/python_code/private.py", line 9, in __getattr__
return self.name
File "/Users/code/python_code/private.py", line 9, in __getattr__
return self.name
File "/Users/code/python_code/private.py", line 9, in __getattr__
return self.name
[Previous line repeated 992 more times]
File "/Users/code/python_code/private.py", line 12, in __getattribute__
print('__getattribute__')
RecursionError: maximum recursion depth exceeded while calling a Python object
1.4注意self.__dict__[name]
在
__getattr__
方法中使用self.__dict__[name]
是允许的,但要小心使用,因为这绕过了常规的属性查找机制。直接使用self.__dict__[name]
会直接访问对象的字典,绕过了__getattr__
和__getattribute__
的调用。
class A:
def __init__(self, x) -> None:
self.x = x
def __getattr__(self, name):
print(f'__getattr__ 【{name}】')
return self.__dict__[name]
def __getattribute__(self, name: str):
print(f'__getattribute__ 【{name}】')
return super().__getattribute__(name)
a = A(1)
print(a.x)
print(a.z)
__getattribute__ 【x】
1
__getattribute__ 【z】
__getattr__ 【z】
__getattribute__ 【__dict__】
Traceback (most recent call last):
File "/Users/lxd670/code/python_code/asyncio_test/python类/private.py", line 15, in <module>
print(a.z)
File "/Users/lxd670/code/python_code/asyncio_test/python类/private.py", line 7, in __getattr__
return self.__dict__[name]
KeyError: 'z'
1.5最终用法
当使用
obj.undefined_attr
时,如果__getattribute__
存在,它会首先被调用。只有在__getattribute__
中抛出AttributeError
异常时,__getattr__
才会被调用。如果
__getattribute__
不存在,或者它没有引发AttributeError
异常,__getattr__
不会被调用。
class A:
def __init__(self, x) -> None:
self.x = x
def get_default_value(self):
return None
def __getattr__(self, name):
if name in self.__dict__:
return self.__dict__[name]
else:
print(f"__getattr__ called for {name}")
# 返回一个默认值或引发 AttributeError
return self.get_default_value()
def __getattribute__(self, name: str):
# 进行拦截或者过滤
print(f'__getattribute__ 【{name}】')
return super().__getattribute__(name)
a = A(1)
print(a.x)
print(a.z)
__getattribute__ 【x】
1
__getattribute__ 【z】
__getattribute__ 【__dict__】
__getattr__ called for z
__getattribute__ 【get_default_value】
None