__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
posted @ 2024-01-07 02:50  lxd670  阅读(5)  评论(0编辑  收藏  举报