python类的实现中有关__setattr__原理问题

python类的实现中有关__settar__原理问题

具体解决思路

问题代码段:

    class CustomAttributes:
        def __init__(self):
            self._attributes = {}

        def __setattr__(self, name, value):
            # 允许设置名为 '_attributes' 的属性,这是实现所必需的  
            if name == '_attributes':
                super().__setattr__(name, value)
                # 对于其他属性,添加它们到 _attributes 字典中
            else:
                self._attributes[name] = value

        def __getattr__(self, name):
            # 尝试从 _attributes 字典中获取属性  
            if name in self._attributes:
                return self._attributes[name]
                # 如果属性不存在,则调用默认的 __getattr__ 实现
            else:
                raise AttributeError(f"'CustomAttributes' object has no attribute '{name}'")

            # 创建一个 CustomAttributes 实例


    obj = CustomAttributes()

    # 设置属性  
    obj.name = 'Alice'
    obj.age = 35

    # 访问属性  
    print(obj.name)  # 输出: Alice  
    print(obj.age)  # 输出: 35

在这段代码中,我发现特殊方法__setattr__中有段未知代码 super().__setattr__(name, value),我要对其功能进行一个测试。

  1. 在代码测试中加入_attributes的value。

    import CustomAttributes
    
    obj = CustomAttributes()
    
    # 设置属性  
    obj.name = 'Alice'
    obj.age = 35
    obj._attrubites = 10
    
    # 访问属性  
    print(obj.name)  
    print(obj.age)
    print(obj._attributes)
    '''
    输出:
    Traceback (most recent call last):
        File "C:\Users\GOD\Desktop\301.py", line 32, in <module>
            print(obj.name)
                ^^^^^^^^
        File "C:\Users\GOD\Desktop\301.py", line 15, in __getattr__
            if name in self._attributes:
            ^^^^^^^^^^^^^^^^^^^^^^^^
        TypeError: argument of type 'int' is not iterable
    '''
    
    import CustomAttributes
    
    obj = CustomAttributes()
    
    # 设置属性  
    obj.name = 'Alice'
    obj.age = 35
    obj._attributes = 10
    
    # 访问属性  
    print(obj._attributes)  # 输出:10
    
    

    由此得知,super().__setattr__(name, value)这段代码的功能是使 _attributes的key可以覆盖原属性dict。

  2. 删掉这段代码,测试其是否为重要组成部分。

    class CustomAttributes:
        def __init__(self):
            self._attributes = {}
    
        def __setattr__(self, name, value):
            self._attributes[name] = value
    
        def __getattr__(self, name):
            # 尝试从 _attributes 字典中获取属性
            if name in self._attributes:
                return self._attributes[name]
                # 如果属性不存在,则调用默认的 __getattr__ 实现
            else:
                raise AttributeError(f"'CustomAttributes' object has no attribute '{name}'")
    
            # 创建一个 CustomAttributes 实例
    
    
    obj = CustomAttributes()
    
    '''
    输出:
    Traceback (most recent call last):
        File "C:\Users\GOD\Desktop\301.py", line 19, in <module>
            obj = CustomAttributes()
                ^^^^^^^^^^^^^^^^^^
        File "C:\Users\GOD\Desktop\301.py", line 3, in __init__
            self._attributes = {}
            ^^^^^^^^^^^^^^^^
        File "C:\Users\GOD\Desktop\301.py", line 6, in __setattr__
            self._attributes[name] = value
            ^^^^^^^^^^^^^^^^
        File "C:\Users\GOD\Desktop\301.py", line 10, in __getattr__
            if name in self._attributes:
                    ^^^^^^^^^^^^^^^^
        File "C:\Users\GOD\Desktop\301.py", line 10, in __getattr__
            if name in self._attributes:
                    ^^^^^^^^^^^^^^^^
        File "C:\Users\GOD\Desktop\301.py", line 10, in __getattr__
            if name in self._attributes:
                    ^^^^^^^^^^^^^^^^
        [Previous line repeated 743 more times]
    RecursionError: maximum recursion depth exceeded
    '''
    

    由此得知,去除掉这段代码会导致触发无限递归,
    __init__ 方法在创建 CustomAttributes 实例时被调用时,它尝试设置 _attributes 字典。这触发了 __setattr__ 方法的调用。
    __setattr__ 方法内部,当尝试访问 self._attributes 时,由于 _attributes 字典尚未完全初始化,Python 会调用 __getattr__ 方法来获取该属性。
    然后,__getattr__ 方法又尝试检查 name 是否在 self._attributes 中,这再次触发了 __getattr__ 的调用,从而形成了无限递归。

总结

  1. 由两个实验得之:
    uper().__setattr__(name, value)允许你绕过当前类中的 __setattr__方法,并直接调用其父类的 __setattr__方法。
  2. __init__方法创建实例时被调用时,会调用 __setattr__方法去设置实例的属性,当在 __setattr__方法内部尝试访问,由于 _attributes 字典尚未完全初始化,Python 会调用 __getattr__ 方法来获取该属性,从而造成递归循环。
posted @ 2024-02-15 21:17  LPF05  阅读(3)  评论(0编辑  收藏  举报