社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 程序配置文件管理的最佳工程实践

马哥Linux运维 • 2 年前 • 337 次点击  

背景

最近在结合 Python-3.12.0a6 版本开发一个多线程架构的后台服务;服务启动时会读取配置文件,并且要求所有线程共享同一份配置。

服务本身直接通过 http 接口来动态调整配置项的值,还要做到服务退出之后持久化配置项到配置文件。

一开始以为这个用 Python 写也会要用几百行 ,最后发现完成核心功能就只需要不到 50 行,Python 牛逼!!!


需求一:支持简单的配置项

假设我们目前只支持 name 和 port 两个配置项,多支持几个不难,只是不方便演示。

"""实例配置管理"""
from dataclasses import dataclass

@dataclassclass Config(object): name:str= "mysql" port:int = 3306

看起来是没问题了,下面可以试用一下,也顺带引导出第二个需求。

In [6]: a = Config()
In [7]: b = Config()
In [8]: id(a)Out[8]: 4407850896
In [9]: id(b)Out[9]: 4407852496

可以看到两个配置对象的 ID 值不一样。由于配置文件只有一个,我们希望配置对象也只有一个。


需求二:配置对象全局唯一

交代一个背景,解释器在做 import 的时候是单一线程在跑的。有了这个前提我们可以少写一些加锁的代码,能少写一行算一行吧。

"""实例配置管理"""
from dataclasses import dataclass

@dataclassclass Config (object): name:str= "mysql" port:int = 3306
_instance = None
# 单例模式 def __new__(cls, *args, **kw): if cls._instance is None: cls._instance = object.__new__(cls, *args, **kw) return cls._instance

用 Python 就是这么的简单,几行代码就搞定了。但是还是要测试一下顺带引导出下一个需求。

In [4]: a = Config()
In [5]: b = Config()
In [6]: id(a)Out[6]: 4414751568
In [7]: id(b)Out[7]: 4414751568

现在配置对象已经是单例了,但还有一个问题,它的每个配置项的值都是默认值,我们当然是希望它在创建对象的时候是使用配置文件中的值啦。下面看需求三怎么实现。


需求三:根据配置文件构造配置对象

假设我们的配置文件被 “持久化” 到了 /tmp/config.json ,现在就可以写读取配置文件并更新配置对象值的代码了。

"""实例配置管理"""
import jsonimport loggingfrom pathlib import Pathfrom dataclasses import dataclass

@dataclassclass Config(object): name:str= "mysql" port:int = 3306
_instance = None
# 单例模式 def __new__(cls, *args, **kw): if cls._instance is None: cls._instance = object.__new__(cls, *args, **kw) return cls._instance
# 读取配置文件 def __post_init__(self): """如果配置文件存在就用配置文件中的值,覆盖默认值。在这个过程中如果遇到异常就保持默认值 """ if (config_file:=Path("/tmp/config.json")) and config_file.exists(): try: with open(config_file) as f: json_data = json.loads(f.read()) self.__dict__.update(json_data) except Exception as err: pass else: logging.warn("config file '{}' not exists. well using defautl values .".format(config_file))

假设我们的配置文件内容是这样的。

cat /tmp/config.json {  "name": "trump",  "port": 8848}

下面的测试一下

In [2]: a = Config()
In [3]: aOut[3]: Config(name='trump', port=8848)
In [4]: b = Config()
In [5]: bOut[5]: Config(name='trump', port=8848)
In [6]: a == bOut[6]: True

可以看到 name 和 port 已经没有使用默认的 "mysql" 和 3306 了,而是使用了配置文件中的值。

到这里我们只剩下最后一个需求,就是在程序退出的时候,把配置对象的值更新回配置文件。这个就看需求四怎么写。


需求四:程序退出前自动持久化配置对象到配置文件

解释器在退出前有个钩子(atexit),我们可以在这里指定回调函数,这个时候保存配置文件再适合不过。

"""实例配置管理"""
import jsonimport atexitimport loggingfrom pathlib import Pathfrom dataclasses import dataclass, asdict

@dataclassclass Config(object): name:str= "mysql" port:int = 3306
_instance = None
# 单例模式 def __new__(cls, *args, **kw): if cls._instance is None: cls._instance = object.__new__(cls, *args, **kw) return cls._instance
# 读取配置文件 def __post_init__(self): """如果配置文件存在就用配置文件中的值,覆盖默认值;在这个过程中如果遇到异常就保持默认值。程序退出时持久到到配置到文件。 """ if (config_file:=Path("/tmp/config.json")) and config_file.exists(): try: with open(config_file) as f: json_data = json.loads(f.read()) self.__dict__.update(json_data) except Exception as err: pass else: logging.warn("config file '{}' not exists. well using defautl values .".format(config_file))
# 程序退出时保存配置到配置文件 /tmp/config.json def sync_to_disk():            """ """ json_str = json.dumps(asdict(self), indent=4) with open(config_file, 'w') as f: logging.warning("save configs to '{}' ".format(config_file)) f.write(json_str)
atexit.register(sync_to_disk)

验证一下

In [1]: from appconfig import Config
In [2]: a = Config()
In [3]: a.nameOut[3]: 'trump'
In [4]: a.name = "hello-world"
In [5]: exit()WARNING:root:save configs to '/tmp/config.json'

看日志是已经把配置项更新回配置文件了,但是还是 cat 确认一下为好。

cat /tmp/config.json {    "name": "hello-world",    "port": 8848}

可以看到确实已经把配置项的值更新到文件了。

链接:https://cloud.tencent.com/developer/article/2269479

(版权归原作者所有,侵删)



Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/154338
 
337 次点击