Redian新闻
>
Python 程序配置文件管理的最佳工程实践

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

公众号新闻

背景

最近在结合 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实现财务报表自动化的实用指南如何在 Ubuntu 和其他 Linux 下安装 IDLE Python IDE | Linux 中国南澳散记 (增订本) :第二十一章:买书(下)军政、训政、宪政3天速成!清华大学出品《Python漫画教学书》!零基础留学生码住为什么Python如此受欢迎?为什么 Python 如此受欢迎?有了这 5 个自动化运维场景,让你用 Python 写脚本更爽!迎难而上,腾讯云混沌工程实践之道揭秘Go/Rust挑战Java/Python地位太强啦!!!ChatGPT 能上传文件了,能执行 Python 代码啦!看完哈佛学霸收藏的Python课程,才懂得人和人的差距Flask之父凭一己之力击败各种GPT,称Python包管理比LLM更火热这款编译器能让Python和C++一样快:最高提速百倍,MIT出品Artipie:可用于 Python 的开源仓库管理器 | Linux 中国小白速成!清华大学出品《Python漫画教学书》,零基础码住!轻松啃下砖头!5分钟精读带你入门Python神作小鳄鱼!(第一周)Python上线最新证书:可直接挂LinkedIn、留学生7天能拿!麻省理工Python增强编译器Codon 让Python像C\\C++一样高效你相信预言吗?提升字节规模化效能的平台化思路|字节跳动平台工程实践Python 中的变量:概念与示例 | Linux 中国ELF 文件、镜像(Image)文件、可执行文件、对象文件详解从零开始的python教程(1):全面又好用的学习资料春假遛娃兼打卡“让 Python 快 5 倍”最新计划:优化解释器和内存管理让 Python 拥有 C/C++ 一样的速度,编译神器 Codon 发布!Python多进程学习比较真正接近和继承孔子的儒学是心学Flask 之父凭一己之力击败各种 GPT,称 Python 包管理比 LLM 更火热让K8s更加高效:服务暴露和Ingress七层代理的最佳实践!太炸裂了!做Python私活赚得比主业还多!这款编译器能让Python和C++一样快!最高提速百倍,MIT出品!5个常见运维场景,用这几个Python脚本就够了!Python官宣:这张可挂LinkedIn的证书,留学生7天可拿
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。