如何优雅使用Python Dataclass单例实现TOML配置管理?
问题原因分析
你遇到的AttributeError核心原因是把单例的_instance定义成了dataclass的实例字段,而非类属性。dataclass的字段属于实例对象,类本身不会持有这个属性,所以cls._instance自然找不到。另外frozen=True的设置适合配置类的不可变需求,但并非当前错误的直接原因。
优雅的单例Dataclass配置实现
下面是修正后的方案,兼顾单例特性、不可变配置、TOML加载逻辑:
import dataclasses import typing import pathlib import tomllib import logging from threading import Lock # 假设这些子配置类已定义 @dataclasses.dataclass(frozen=True) class Target: pass @dataclasses.dataclass(frozen=True) class FFmpeg: pass @dataclasses.dataclass(frozen=True) class Whisper: pass @dataclasses.dataclass(frozen=True) class Translate: pass @dataclasses.dataclass(frozen=True) class Srt: overwrite: bool bilingual: bool @dataclasses.dataclass(frozen=True) class Log: level: int count: int size: int @dataclasses.dataclass(frozen=True) class Config: # 类属性:存储单例实例,不属于dataclass字段 _instance: typing.Optional[typing.Self] = None # 线程锁:保证多线程环境下的单例安全性(单线程场景可移除) _lock: Lock = Lock() Targets: list[Target] FFmpeg: typing.Optional[FFmpeg] Whisper: typing.Optional[Whisper] Translate: typing.Optional[Translate] Srt: typing.Optional[Srt] Log: typing.Optional[Log] @classmethod def get_instance(cls) -> typing.Self: # 双重检查锁定,减少锁开销 if cls._instance is None: with cls._lock: if cls._instance is None: # 加载TOML配置文件 config_path = pathlib.Path().absolute().joinpath("config.toml") with open(config_path, "rb") as f: data = tomllib.load(f) # 初始化子配置 log_config = Log(level=logging.DEBUG, count=0, size=0) targets: list[Target] = [] srt_config = Srt( overwrite=data['srt']['overwrite'], bilingual=data['srt']['bilingual'] ) # 根据实际TOML结构补全其他子配置初始化 ffmpeg_config = FFmpeg() whisper_config = Whisper() translate_config = Translate() # 创建不可变的Config实例 cls._instance = Config( Targets=targets, FFmpeg=ffmpeg_config, Whisper=whisper_config, Translate=translate_config, Srt=srt_config, Log=log_config ) return cls._instance # 全局配置实例,仅初始化一次 CONFIG: Config = Config.get_instance()
关键改进点
- 类属性存储单例:把
_instance从dataclass字段移为类属性,确保类能直接访问,彻底解决AttributeError。 - 不可变配置:保留
frozen=True,保证配置实例创建后无法被修改,符合配置的只读特性。 - 线程安全:添加
Lock和双重检查锁定,避免多线程环境下多次初始化的问题(单线程场景可移除锁代码)。 - 清晰的初始化逻辑:把单例获取和配置加载封装在
get_instance方法中,调用更直观,避免递归错误。
额外说明
如果需要支持配置重新加载,可以添加reload类方法,先重置_instance再调用get_instance(注意保持线程安全);子配置类也建议用frozen=True,保证整个配置体系的不可变性。
内容的提问来源于stack exchange,提问作者Panic
相关产品推荐
相关产品推荐

