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

Python单例Config类实现失败:递归错误成因咨询

问题分析与修复

问题描述

尝试为配置类实现单例模式:_Config类负责加载config.yaml文件,Config作为其子类,预期每次实例化返回唯一实例。但执行config1 = Config()时触发RecursionError(递归深度超限)。

错误根源

递归的核心原因是Config.__init__中的hasattr(self, '_config')调用,结合_Config.__getattr__的实现形成了无限循环:

  1. 第一次实例化Config时,__init__先检查hasattr(self, '_config'),此时_config还未被初始化(super().__init__()尚未执行);
  2. hasattr会尝试获取self._config,由于_Config重写了__getattr__方法,且此时实例的_config属性不存在,会触发__getattr__;
  3. 在__getattr__中,当name == '_config'时,代码返回self._config,这又会再次触发__getattr__(属性仍未初始化),形成无限递归。

代码问题点

  • _Config.__getattr__处理_config属性时,直接使用self._config会触发自身调用;
  • Config.__init__用hasattr检查属性,会间接触发__getattr__,而此时_config还未完成初始化。

修复方案

方案1:修改属性检查逻辑

用直接检查实例__dict__的方式替代hasattr,避免触发__getattr__:

def __init__(self):
    if '_config' not in self.__dict__:
        logger.debug('no _config')
        super().__init__()

方案2:修复__getattr__的递归问题

当访问_config时,用object.__getattribute__直接获取属性,绕过__getattr__的递归调用:

def __getattr__(self, name):
    if name == '_config':
        return object.__getattribute__(self, '_config')
    
    if name not in self._config:
        raise AttributeError(name)

    item = self._config.get(name)
    if item is None:
        raise NotImplementedError(f'"{name}" not found in config.yaml')
    if isinstance(item, dict):
        item = _Config(config=item)
    return item

方案3:简化单例实现

直接在__new__中完成初始化,避免__init__重复执行:

class Config(_Config):
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            logger.debug('Creating a new Config instance')
            cls._instance = super().__new__(cls)
            super(Config, cls._instance).__init__()
        return cls._instance

    def __init__(self):
        # 空实现防止多次初始化
        pass

验证修复

修改后执行config1 = Config(),将不再触发递归错误,且每次实例化都会返回同一个单例对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:49:54