Python中如何优雅地读取并支持运行时更改的配置文件?

本来选的 python 节点,
可能因为"大家", “都是”, “怎样” “?” 这些关键字, 被移动到调查节点了
有个需求是可能会在运行中修改配置文档, 然后其他同样引用了该配置的模块也能拿到新的值
代码在原帖内


Python中如何优雅地读取并支持运行时更改的配置文件?
25 回复

缓存中读取,避免脏读即可


importlib.reload 配合单例模式来热更新配置最直接。下面这个 ConfigManager 类能搞定:

import importlib
import threading
from typing import Any, Dict

class ConfigManager:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
                cls._instance._config_module = None
                cls._instance._config_dict = {}
                cls._instance._load_config()
        return cls._instance
    
    def _load_config(self):
        """加载或重新加载配置文件"""
        try:
            # 假设配置文件是 config.py
            config_module = importlib.import_module('config')
            if self._config_module:
                importlib.reload(config_module)
            else:
                self._config_module = config_module
            
            # 提取所有大写配置项
            self._config_dict = {
                key: getattr(config_module, key)
                for key in dir(config_module)
                if key.isupper() and not key.startswith('_')
            }
        except ImportError:
            # 处理配置文件不存在的情况
            self._config_dict = {}
    
    def get(self, key: str, default: Any = None) -> Any:
        """获取配置值"""
        return self._config_dict.get(key, default)
    
    def reload(self):
        """重新加载配置文件"""
        with self._lock:
            self._load_config()
    
    def all_configs(self) -> Dict[str, Any]:
        """获取所有配置"""
        return self._config_dict.copy()

# 使用示例
config = ConfigManager()

# 获取配置
db_host = config.get('DB_HOST', 'localhost')
api_key = config.get('API_KEY')

# 需要时重新加载
config.reload()

配置文件 config.py 这样写:

# config.py
DB_HOST = "localhost"
DB_PORT = 5432
API_KEY = "your_api_key_here"
DEBUG = True

这样你在运行时改 config.py 里的值,调用 config.reload() 就能立刻生效。用单例确保全局只有一个配置实例,加锁避免并发问题。

简单说就是:用 importlib.reload 加热更新方法最省事。

关键是如何优雅的避免脏读呢?

配置中心服务了解一下?

正常应该是接受处理 USR1 信号,平滑重载更新配置缓存吧,你总不能开着文件句柄一直读,太难受了…

我原帖内最后就是这样, 启动时先载入配置缓存, 有修改时先修改文档, 在重新加载到缓存, 但我的方式感觉读取配置时怪怪的

小工具用不上这个吧?

增加一个接口,被调用时从外部拉取配置更新到内存里,我的 openrestry 就是这么操作的

那你在其他地方引用该配置的时候, 是用的时候在引用吗?
还是在开头就引用了?
也就是
from conf import xxx

class X():
def xx():
XXX
还是

class X():
def xx():
from conf import xxx
XXX

赞同#4

简化来说的话就是 global config = {} , 全局变量,不过细化来说的话还涉及到读写锁

一般是 HUP 信号吧

抱歉, 我去认真看一下 USR1 和如何应用

我原帖的举例就是 gloabl conf=dict(), 不过没涉及到独写锁, 你提醒了我

openrestry 在 init-worker 阶段可以注册定时器,你这样相当于每个请求都耦合了一次配置刷新的操作

看 USR1 的应用, 以及 uwsgi 的配置文档重启, 但那个适合静态的配置, 改一次重启一次服务,
可我只想依赖这个配置的模块能获取到新的值, 并不想重启整个服务, 因为我这只是一个工具, 并不会涉及到并发之类的

不是重启整个服务啊,python 里用 signal 模块可以捕获到 USR1 或是上面 11#提到的 HUP 信号,然后你自己处理就好了,并不是让系统去给你重启

可以借鉴配置中心…或者说可以把配置写在数据库里面,系统文件里面都行…

合理的事务控制,中心化配置

用 apollo

inotify 监控配置文件,有变更就调用重载配置函数

配置文件的库的库最好支持参数设置是否可以热重载

请使用 python 里最牛逼的配置文件库 oslo.cfg

看 openstack 时, 看过 oslo 的用法, 没有仔细研究过, 确实可以试试

这个有点大材小用了


熟悉以后 只要你还用 py, 这玩意可以用不少年
不 py 语言允许都可以抄一份

好! 之后也要搞 openstack, 大不了我把离线包加到依赖包里, 装我这个工具的时候, 直接装进去

回到顶部