Python 中如何在类初始化时加载 JSON 配置文件

本文详解如何通过 `__init__` 方法自动加载外部 json 配置文件,并将配置项动态注入实例属性,实现简洁、可复用的配置管理。

在 Python 开发中,将配置与代码分离是良好实践。一种常见方式是使用 JSON 文件存储配置,并在类初始化时自动读取。你提供的 ConfigHandler 类思路正确,但存在两个关键问题:未实例化对象(导致 __init__ 从未执行),以及硬编码绝对路径("/config.json" 在多数环境下不可靠,易引发 FileNotFoundError)。

下面是一个改进后的完整教程实现:

✅ 正确用法:先实例化,再访问属性

假设你的项目结构如下:

/project_root/
├── config.json
├── ConfigHandler.py
└── main.py

config.json 内容示例:

{
  "database_url": "sqlite:///app.db",
  "debug": true,
  "max_retries": 3,
  "api_timeout": 15.0
}

ConfigHandler.py(增强健壮性):

import os
import json

class ConfigHandler:
    def __init__(self, config_path=None):
        # 默认尝试从当前工作目录加载;更推荐传入显式路径
        if config_path is None:
            config_path = os.path.join(os.getcwd(), "config.json")

        print(f"Initializing config from: {config_path}")

        if os.path.exists(config_path):
            try:
                with open(config_path, "r", encoding="utf-8") as f:
                    config_data = json.load(f)
                # 安全地将字典键转为实例属性(避免覆盖内置方法)
                for key, value in config_data.items():
                    if not key.startswith("__") and isinstance(key, str):
                        setattr(self, key, value)
                print("✅ Config loaded successfully.")
            except json.JSONDecodeError as e:
                raise ValueError(f"Invalid JSON in {config_path}: {e}")
            except Exception as e:
                raise RuntimeError(f"Failed to load config: {e}")
        else:
            raise FileNotFoundError(f"Config file not found: {config_path}")
? 关键说明: 使用 setattr(self, key, value) 替代直接赋值 self.__dict__ = ...,更安全且兼容 @property 和描述符; 添加异常处理,避免静默失败; 显式 with open(...) 确保文件句柄正确释放; 支持传入自定义 config_path,提升可测试性与灵活性。

? 在主程序中使用

main.py:

from ConfigHandler import ConfigHandler

# ✅ 正确:创建 ConfigHandler 实例 → 触发 __init__ → 自动加载并设置属性
config = ConfigHandler()

# 直接通过点号访问配置项(无需 dict 键语法)
print("DB:", config.database_url)
print("Debug mode:", config.debug)
print("Retries:", config.max_retries)

# 可用于其他类的初始化
class MainApp:
    def __init__(self, config):
        self.config = config  # 保存引用
        print(f"MainApp started with timeout {self.config.api_timeout}s")

app = MainApp(config)

⚠️ 注意事项与最佳实践

  • 不要在类体中直接赋值 config = ConfigHandler()(如 class MainApp: config = ConfigHandler()),这会在模块导入时执行,而非实例化时,且可能因路径上下文错误而失败;
  • 始终通过 instance = ConfigHandler(...) 实例化,确保 __init__ 被调用;
  • ? 路径建议:使用 os.path.join(os.path.dirname(__file__), "config.json") 获取相对于模块的路径,比 os.getcwd() 更可靠;
  • ? 敏感配置:JSON 不支持注释且无加密能力,生产环境建议改用 .env(配合 python-dotenv)或 YAML + 密钥管理;
  • ? 单元测试友好写法:将 config_path 作为参数传入 __init__,便于 mock 或切换测试配置。

通过以上方式,你就能构建一个轻量、健壮、符合 Python 惯例的配置加载器——既保持代码清晰,又具备良好的可维护性与可扩展性。