用了 Python 这个新特性之后,我的配置文件管理终于变得清爽了.
干了几年Python开发,配置文件管理一直是个让我头疼的问题。以前项目小还好说,一个config.py文件就能搞定。项目大了之后,不同环境、不同模块的配置混在一起,改一个地方要翻半天。有人推荐用环境变量,有人推荐用yaml,我都试过,总觉得不够顺手。
直到我发现了一个新特性,叫dataclass。这个特性其实Python 3.7就引入了,我竟然现在才拿来管配置文件。用了之后,感觉就像给散乱的工具找了个整齐的工具箱。
以前我写配置文件,要么是字典,要么是普通类。字典的缺点是键名容易写错,IDE也没法给提示。普通类呢,得写一堆__init__方法,加上类型注解、默认值,代码看着就啰嗦。比如一个数据库配置,我得写成:
class DBConfig: def __init__(self, host, port, user, password):
self.host = host
self.port = port
self.user = user
self.password = password
这还算简单的。如果有十几个配置类,每个类都要这么写,光初始化代码就占几百行。更麻烦的是,万一要加个字段,所有用到的地方都得改。
换成dataclass之后,同样的配置变成这样:
from dataclasses import dataclass @dataclass
class DBConfig:
host: str
port: int
user: str
password: str
看到区别了吗?少了一堆重复的self.xxx = xxx。Python自动帮我生成__init__、__repr__这些方法。如果一个字段有默认值,直接等于就行,比如port: int = 3306。IDE还能识别类型,写代码的时候自动补全,键盘敲着都响亮了。
刚开始我还想,就这点区别,没什么了不起的。真到项目里一用,发现好处不止这些。我最喜欢的一个功能是不可变配置。用frozen=True参数,配置对象一旦创建就不能改了。比如:
@dataclass(frozen=True) class AppConfig:
debug: bool = False
max_connections: int = 100
如果代码里有人不小心改了config.debug = True,Python立刻报错。这对于配置文件来说太重要了。以前被同事在业务代码里偷偷修改全局配置坑过好几回,查bug查到半夜。现在想改配置?必须走配置文件,必须重启服务。副作用少了,心里踏实多了。
还有一个偷懒的技巧。dataclass可以嵌套。我把项目配置分成几个模块:数据库、缓存、外部API、日志。每个模块一个dataclass,然后用一个总配置类把它们包起来:
@dataclass class Config:
db: DBConfig
redis: RedisConfig
api: ApiConfig
log: LogConfig
读配置的时候,一行代码加载JSON或YAML文件,转成dataclass实例。访问配置就变成了config.db.host,清晰得像菜单。找配置的时候,点一下就找到了,不用满文件搜索。
身边有几个同事看了我的写法,也开始用dataclass管理配置。有一个哥们儿说他最大的感受是重构成本降低了。以前改一个配置字段的名字,得手动搜索所有引用,改完跑测试才发现有漏的。现在改dataclass的字段名,IDE自动关联,编译期就能发现问题。
说实话,用了这么久,我觉得dataclass最适合管理那些结构稳定、类型明确的配置。如果配置动不动就改结构,或者字段类型不固定,那还得用字典。但大部分项目的配置,比如数据库连接、服务端口、开关参数,都是固定的。用dataclass管理,正好发挥它的优势。
现在打开我的项目目录,看到那个config.py文件,里面一排排整齐的@dataclass,心情确实清爽不少。早期代码那种乱糟糟的感觉不见了。推荐你也试试,从最简单的配置开始改造。用上之后,你会发现配置文件管理这件事,其实可以很简单。