在Python项目开发中,配置文件通常用于存储数据库连接信息、服务端口、功能开关等可变参数,合理的配置文件加载设计能够避免重复加载、格式不兼容、配置更新不及时等问题,提升项目的可维护性和扩展性。不同的项目规模和需求对应不同的设计模式,下面介绍几种常用的配置文件加载设计模式及其实现方式。

单例模式:避免配置重复加载
单例模式的核心是保证一个类只有一个实例,并且提供全局访问点。在配置文件加载场景中,配置内容通常在项目运行期间不会频繁变化,重复加载配置会浪费IO资源和计算资源,使用单例模式可以让配置类全局唯一,多次获取配置时只加载一次配置文件。
下面是基于__new__方法实现的单例配置加载类,支持加载JSON格式的配置文件:
import json
import os
class ConfigSingleton:
# 存储唯一实例
_instance = None
# 存储配置内容
_config = None
def __new__(cls, config_path=None):
# 如果实例不存在则创建
if cls._instance is None:
cls._instance = super().__new__(cls)
# 初始化时加载配置
if config_path is None:
# 默认配置文件路径
config_path = os.path.join(os.path.dirname(__file__), "config.json")
cls._instance.load_config(config_path)
return cls._instance
def load_config(self, config_path):
# 加载JSON配置文件
with open(config_path, "r", encoding="utf-8") as f:
self._config = json.load(f)
def get(self, key, default=None):
# 获取配置项,不存在则返回默认值
return self._config.get(key, default)
# 使用示例
if __name__ == "__main__":
# 第一次获取实例,会加载配置
config1 = ConfigSingleton("test_config.json")
# 第二次获取实例,直接返回已创建的实例,不会重复加载
config2 = ConfigSingleton()
print(config1 is config2) # 输出True,说明是同一个实例
print(config1.get("db_host", "127.0.0.1"))
工厂模式:适配多格式配置文件
如果项目中需要支持多种格式的配置文件,比如JSON、YAML、INI等,使用工厂模式可以将配置加载的逻辑和具体的格式实现解耦,后续新增配置格式时只需要新增对应的加载类,不需要修改原有代码,符合开闭原则。
首先定义配置加载的抽象基类,然后实现不同格式的加载子类,最后通过工厂类根据配置文件的后缀返回对应的加载实例:
import json
import os
import configparser
# 抽象配置加载基类
class ConfigLoader:
def load(self, config_path):
raise NotImplementedError("子类需要实现load方法")
# JSON格式配置加载类
class JsonConfigLoader(ConfigLoader):
def load(self, config_path):
with open(config_path, "r", encoding="utf-8") as f:
return json.load(f)
# INI格式配置加载类
class IniConfigLoader(ConfigLoader):
def load(self, config_path):
config = configparser.ConfigParser()
config.read(config_path, encoding="utf-8")
# 转换为字典格式方便使用
result = {}
for section in config.sections():
result[section] = dict(config[section])
return result
# 配置加载工厂类
class ConfigLoaderFactory:
@staticmethod
def get_loader(config_path):
# 根据文件后缀选择对应的加载器
ext = os.path.splitext(config_path)[1].lower()
if ext == ".json":
return JsonConfigLoader()
elif ext == ".ini":
return IniConfigLoader()
else:
raise ValueError(f"不支持的配置文件格式: {ext}")
# 使用示例
if __name__ == "__main__":
# 加载JSON配置
json_loader = ConfigLoaderFactory.get_loader("test.json")
json_config = json_loader.load("test.json")
print("JSON配置内容:", json_config)
# 加载INI配置
ini_loader = ConfigLoaderFactory.get_loader("test.ini")
ini_config = ini_loader.load("test.ini")
print("INI配置内容:", ini_config)
观察者模式:实现配置动态更新
如果项目需要支持配置文件修改后动态生效,不需要重启服务就能应用新的配置,可以使用观察者模式。观察者模式定义了一种一对多的依赖关系,当配置发生变化时,所有依赖配置的对象都会收到通知并更新自身状态。
实现思路是配置加载类作为被观察者,维护一个观察者列表,当检测到配置文件修改时,遍历列表通知所有观察者执行更新逻辑:
import json
import os
import time
from threading import Thread
# 观察者基类
class ConfigObserver:
def update(self, config):
raise NotImplementedError("子类需要实现update方法")
# 被观察者配置类
class ObservableConfig:
def __init__(self, config_path):
self.config_path = config_path
self.observers = []
self._config = None
self._last_modify_time = 0
self.load_config()
# 启动后台线程监听文件变化
self._start_monitor()
def load_config(self):
# 加载配置文件
with open(self.config_path, "r", encoding="utf-8") as f:
self._config = json.load(f)
# 更新最后修改时间
self._last_modify_time = os.path.getmtime(self.config_path)
def add_observer(self, observer):
# 添加观察者
self.observers.append(observer)
def notify_observers(self):
# 通知所有观察者配置更新
for observer in self.observers:
observer.update(self._config)
def _start_monitor(self):
# 后台线程定时检查文件修改时间
def monitor():
while True:
current_modify_time = os.path.getmtime(self.config_path)
if current_modify_time > self._last_modify_time:
self.load_config()
self.notify_observers()
time.sleep(1) # 每秒检查一次
t = Thread(target=monitor, daemon=True)
t.start()
def get(self, key, default=None):
return self._config.get(key, default)
# 具体观察者实现
class DbConfigObserver(ConfigObserver):
def update(self, config):
print(f"数据库配置更新,新的db_host为: {config.get('db_host')}")
# 使用示例
if __name__ == "__main__":
config = ObservableConfig("dynamic_config.json")
# 添加观察者
db_observer = DbConfigObserver()
config.add_observer(db_observer)
# 模拟运行中修改配置文件
time.sleep(3)
# 手动修改dynamic_config.json的db_host值后,观察者会收到通知
print("等待配置文件修改...")
time.sleep(10)
不同设计模式的适用场景
可以根据项目的实际需求选择合适的设计模式,以下是不同模式的适用场景对比:
| 设计模式 | 适用场景 | 优势 |
|---|---|---|
| 单例模式 | 配置格式单一、不需要动态更新、项目规模较小 | 实现简单,避免重复加载,资源占用少 |
| 工厂模式 | 需要支持多种配置格式、后续可能扩展新的配置格式 | 解耦格式实现和加载逻辑,扩展性强 |
| 观察者模式 | 需要配置文件修改后动态生效、不需要重启服务 | 配置更新及时,无需重启服务,灵活性高 |
在实际项目中,也可以将多种设计模式结合使用,比如用单例模式保证配置类唯一,用工厂模式适配多格式,用观察者模式实现动态更新,从而满足更复杂的业务需求。