配置文件读取:configparser 操作 INI 文件、JSON/YAML 配置文件解析

做批量对接参数调试的时候,最头疼的是什么?

我个人觉得,是参数改动后脚本里到处硬编码,改一处崩三处。

配置文件,说白了就是把这些参数抽出来,放到一个单独的文件里。脚本只管读,不改逻辑。

这一章我带你过三种最常见的格式:INI、JSON 和 YAML。每种我都会讲怎么读、怎么写,以及实战中容易踩的坑。

1. INI 文件:老牌简单,但别太信任它

INI 格式结构简单:节(section)、键(key)、值(value)。Python 自带 configparser 模块,不用装第三方包。

先看一个典型的 INI 文件:

[Database]
host = 192.168.1.100
port = 5432
user = admin
password = secret123

[Logging]
level = INFO
output = ./logs/app.log
max_size_mb = 50

读取的代码也很直接:

import configparser

config = configparser.ConfigParser()
config.read('config.ini')

db_host = config['Database']['host']
db_port = config.getint('Database', 'port')  # 转成 int
log_level = config.get('Logging', 'level', fallback='DEBUG')  # 带默认值

print(f"连接 {db_host}:{db_port}")

嗯,这里要注意几个地方。

我曾经踩过这个坑:节(section)名和键名默认都是大小写敏感的,而且 configparser 会把它们转成小写存储。
如果你在 INI 文件里写 Host,然后代码里用 config['Database']['host'] 是能读到的;但反过来,如果文件里用小写,代码里用大写,就会报错。
解决方案:读写统一用小写,或者用 ConfigParser(dict_type=collections.OrderedDict) 之类的高级选项。
小技巧:读取数字、布尔值时,优先用 getint()、getfloat()、getboolean(),避免手动转换时出类型错误。
比如 port = config.getint('Database', 'port') 会比 int(config['Database']['port']) 安全得多。

INI 的缺点很明显:不支持嵌套、不支持数组、不支持多行文本。如果你的参数结构稍微复杂一点,就该考虑 JSON 或 YAML 了。

2. JSON 配置文件:简单可靠,但别忘记编码

JSON 是结构化数据的事实标准,Python 的 json 模块性能高、兼容性好。

一个典型的 JSON 配置文件:

{
  "server": {
    "host": "0.0.0.0",
    "port": 8080,
    "workers": 4
  },
  "database": {
    "host": "192.168.1.100",
    "port": 5432,
    "pool_size": 10
  },
  "log_level": "INFO"
}

读取方法:

import json

with open('config.json', 'r', encoding='utf-8') as f:
    config = json.load(f)

host = config['server']['host']
port = config['server']['port']

写配置文件也简单:

with open('config.json', 'w', encoding='utf-8') as f:
    json.dump(config, f, ensure_ascii=False, indent=2)
核心要点:
  • 读写一定用 encoding='utf-8',否则中文会乱码。
  • ensure_ascii=False 让中文显示为中文,否则会被转成 \uXXXX。
  • indent=2 让 JSON 文件有换行缩进,方便人眼阅读。

我在项目中遇到过一个奇怪问题:同一个 JSON 文件,在 Linux 下跑正常,在 Windows 下就报编码错误。检查后发现 Windows 系统默认编码是 GBK,打开文件时没指定 utf-8 导致。从此我写代码一律显式声明 encoding。

3. YAML 配置文件:优雅但小心「序列化炸弹」

YAML 是目前 DevOps、Kubernetes 等领域最流行的配置格式,支持锚点、引用、嵌套,可读性极强。

Python 里处理 YAML 需要安装 PyYAML:

pip install pyyaml

YAML 文件示例:

server:
  host: 0.0.0.0
  port: 8080
  workers: 4

database:
  host: 192.168.1.100
  port: 5432
  pool_size: 10

log_level: INFO

读取代码:

import yaml

with open('config.yaml', 'r', encoding='utf-8') as f:
    config = yaml.safe_load(f)

host = config['server']['host']
port = config['server']['port']
!这里我要特别强调:永远、永远、永远不要用 yaml.load(),除非你知道自己在做什么。
yaml.load() 默认会构造任意 Python 对象,攻击者可以构造恶意 YAML 远程执行代码。
安全做法永远是 yaml.safe_load(),它能解析标准 YAML 标签,不会执行任意代码。

我曾经接手过一个遗留项目,里面用的 yaml.load,结果被安全扫描扫出来高危漏洞。全组花了三天把每个 YAML 文件审计了一遍,才改成 safe_load。
关于多文档 YAML:如果配置文件用 --- 分隔了多个文档,用 yaml.safe_load_all() 迭代读取所有文档。

4. 三种配置文件的核心对比

特性 INI JSON YAML
标准库支持 configparser json 需安装 PyYAML
嵌套支持 不支持 良好 优秀(锚点/别名)
注释 支持 不支持 支持
多行文本 困难 需转义 原生支持
安全风险 低 低 使用 safe_load
学习成本 极低 低 中等

选哪种?我个人的原则是:

  • 项目只有几个简单参数 → INI 就够,零依赖。
  • 需要做 API 交互、数据交换 → JSON,因为它是 JS 原生格式,跨语言通用。
  • 项目配置复杂、团队多人协作、需要注释 → YAML,可读性最好。

5. 踩坑与最佳实践

实战中我经常看到同学犯的错,列出来给你参考:

  1. 配置文件路径写死。 应该用 os.path.join(os.path.dirname(__file__), 'config.yaml') 或环境变量 + 默认值。
  2. 读配置时不处理异常。 文件不存在、格式错误、键缺失,最好都 try/except 并给出明确报错。
  3. 把密码/密钥明文写在配置文件里。 建议用环境变量或专用的 secrets 管理工具,比如 python-dotenv。
  4. JSON key 重复。 标准 JSON 不允许多个同名 key,但某些解析器会覆盖。如果你用它做配置,确保唯一。
  5. YAML 缩进混用 Tab 和空格。 YAML 严格依赖缩进,建议统一用 2 个空格。
一个实用的小函数:封装统一的配置读取函数,支持多种格式,自动判断类型。
import json
import yaml
import configparser
from pathlib import Path

def load_config(path):
    ext = Path(path).suffix.lower()
    with open(path, 'r', encoding='utf-8') as f:
        if ext == '.json':
            return json.load(f)
        elif ext in ('.yaml', '.yml'):
            return yaml.safe_load(f)
        elif ext == '.ini':
            cfg = configparser.ConfigParser()
            cfg.read_file(f)
            return {s: dict(cfg.items(s)) for s in cfg.sections()}
        else:
            raise ValueError(f"不支持的配置格式: {ext}")
配置文件解析流程(三格式对比) config.ini config.json config.yaml configparser json.load() yaml.safe_load() Python dict (参数已归一化) 配置来源不同,解析方式不同,但最终统一返回 dict,方便后续参数引用

看到这张图应该就清楚了。无论你读哪种格式的文件,最终都会得到一个 Python dict。这样一来,脚本内部的业务逻辑就可以统一用 config['某层']['某键'] 访问,不用管原始文件是 INI 还是 YAML。