You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中如何让__setattr__与__getattr__排除指定属性?

Config类重写__setattr__/__getattr__引发无限递归的解决方法

问题背景

想要实现一个Config类,用来存储和跟踪任意Setting对象,支持像普通属性一样获取和设置(如c.x = 1; print(c.foo),其中x、foo是初始化传入的Setting的name属性)。重写__setattr__和__getattr__后出现无限递归报错。

原代码

#config.py

class Setting:
    def __init__(self, name, value):
        self.name = name
        self.value = value

    def __repr__(self) -> str:
        return f"Setting(name: {self.name}, value: {self.value})"


class Config:
    #_settings = {}
    def __init__(self, settings):
        self._settings = settings

    def __setattr__(self, name, value):
        try:
            self._settings[name].value = value
        except KeyError as err:
            try:
                return super(Config, self).__setattr__(name, value)
            except AttributeError:
                raise err

    def __getattr__(self, name):
        try:
            return self._settings[name].value
        except KeyError as err:
            try:
                return super(Config, self).__getattribute__(name)
            except AttributeError:
                raise err


class Interface:
    def __init__(self):
        settings = {
            s.name: s for s in [
                Setting("x", 1.),
                Setting("y", 2.),
            ]
        }
        self.config = Config(settings)


if __name__ == "__main__":
    i = Interface()
    print(i.config._settings)
    i.config.x = 9.
    print(i.config._settings)
    #print(Config._settings)

报错信息

$ python config.py
Traceback (most recent call last):
  File "config.py", line 52, in <module>
    i = Interface()
  File "config.py", line 48, in __init__
    self.config = Config(settings)
  File "config.py", line 15, in __init__
    self._settings = settings
  File "config.py", line 20, in __setattr__
    self._settings[name].value = value
  File "config.py", line 31, in __getattr__
    return self._settings[name].value
  File "config.py", line 31, in __getattr__
    return self._settings[name].value
  File "config.py", line 31, in __getattr__
    return self._settings[name].value
  [Previous line repeated 991 more times]
RecursionError: maximum recursion depth exceeded

核心原因

在Config.__init__中执行self._settings = settings时,会触发重写的__setattr__方法;而__setattr__里尝试访问self._settings,此时该属性尚未被初始化,会触发__getattr__;__getattr__又试图访问self._settings,进而形成无限递归。

可行解决方案

方案1:初始化时直接调用父类__setattr__设置_settings

修改Config的构造方法,跳过自定义的__setattr__逻辑,直接用父类方法初始化_settings:

class Config:
    def __init__(self, settings):
        # 调用父类方法直接设置,避免触发自定义__setattr__
        super().__setattr__('_settings', settings)

    def __setattr__(self, name, value):
        try:
            self._settings[name].value = value
        except KeyError as err:
            try:
                return super().__setattr__(name, value)
            except AttributeError:
                raise err

    def __getattr__(self, name):
        try:
            return self._settings[name].value
        except KeyError as err:
            try:
                return super().__getattribute__(name)
            except AttributeError:
                raise err

方案2:在__setattr__和__getattr__中优先处理_settings属性

在两个方法开头判断属性名是否为_settings,如果是则直接用父类方法处理,避免进入递归逻辑:

class Config:
    def __init__(self, settings):
        self._settings = settings

    def __setattr__(self, name, value):
        # 优先处理_settings属性,直接调用父类方法
        if name == '_settings':
            return super().__setattr__(name, value)
        try:
            self._settings[name].value = value
        except KeyError as err:
            try:
                return super().__setattr__(name, value)
            except AttributeError:
                raise err

    def __getattr__(self, name):
        # 优先处理_settings属性,直接调用父类方法
        if name == '_settings':
            return super().__getattribute__(name)
        try:
            return self._settings[name].value
        except KeyError as err:
            try:
                return super().__getattribute__(name)
            except AttributeError:
                raise err

方案3:直接操作实例的__dict__(不推荐)

初始化时直接修改实例的__dict__字典,绕过__setattr__方法:

class Config:
    def __init__(self, settings):
        self.__dict__['_settings'] = settings

    # 其余方法保持不变

验证效果

修改后运行测试代码,能够正常输出_settings的内容,且设置i.config.x = 9.后,对应的Setting对象的value会被正确更新,不再出现递归报错。

内容的提问来源于stack exchange,提问作者nieswand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 21:12:05