LunaConf: quick-and-dirty 的 python 命令行传参

拿之前写过的项目来:sweat_droplets:篇 post 捏

Why

你是否在写 python 脚本(跑实验等)时遇到这种问题:

Problem 1
脚本需要传入若干参数。这种参数先用 dataclass/pydantic 定义一个 class Config,需要指定一次类型和默认值;然后再用 argparse 的时候又要再指定一次类型和参数;
Problem 2
argparse 无法轻松地嵌套 Config。比如整个脚本有一个总体的 Config,脚本分为三个模块 train, evaluate, plot, 分别使用 TrainConfig, EvalConfig, PlotConfig。要给它们分别加上 argparse 配置的话,很难区分它们。

What

为了解决以上问题,我开发了 lunaconf 库(pypi, GitHub)。

它主要支持以下内容:

  1. 你只需定义一次 Config,即可自动使用 lunaconf 的命令行接口调整参数;
  2. 支持嵌套的 Config 和嵌套的数组。
  3. 支持 pydantic 的全部功能,比如默认参数、alias、输入约束检查等。

How to use it

使用方法很简单:直接将 lunaconf>=0.5.2 加入依赖即可:

$ uv add lunaconf

下面举个比较完整的例子:比如我这个脚本有 Train 和 Plot 两个子模块,Train 模块中可能输入一个 list 的 Engine 配置。

from lunaconf import LunaConf, lunaconf_cli
from pydantic import Field

class TrainEngineConfig(LunaConf):
    name: str

class TrainConfig(LunaConf):
    engines: list[TrainEngineConfig] = Field(default_factory=list)
    learning_rate: float = 0.01

class PlotConfig(LunaConf):
    columns: int = 2

class Config(LunaConf):
    train: TrainConfig = Field(default_factory=TrainConfig)
    plot: PlotConfig = Field(default_factory=PlotConfig)

if __name__ == "__main__":
    config = lunaconf_cli(Config)
    print(repr(config))
    # do other things

这时尝试运行这个脚本,可以看到打印的参数

Config(train=TrainConfig(engines=[], learning_rate=0.01), plot=PlotConfig(columns=2))

然后简要介绍命令行使用方法:你可以使用如下参数来修改值,比如我要使用三列而非两列

$ python3 main.py \
    plot.columns=3
Config(train=TrainConfig(engines=[], learning_rate=0.01), plot=PlotConfig(columns=3))

还可以使用 .0 等来访问数组,并使用 json 语法来创建一个比较大的对象:

$ python main.py train.engines.0='{ "name": "vllm" }'
Config(train=TrainConfig(engines=[TrainEngineConfig(name='vllm')], learning_rate=0.01), plot=PlotConfig(columns=2))  

同时,还可以用 -T, -J 参数读入已存在的 toml/json 文件,用 -p, -P 参数将当前配置用 toml/json 输出并退出。

除此以外,还有在配置时读入环境变量/读入特殊值(如 NaN 等)的功能。当你想用到时,可以仔细阅读项目的 README.md 文档!

3 个赞

现在我自己写 evaluation 等脚本时都直接用我自己这个库了;确实挺好用的

这个思路很实用,尤其是把“配置定义”和“CLI 参数”合并成一套,做实验脚本会清爽很多 :+1:

我这边看下来最喜欢两点:

1. 对嵌套配置和 list 的支持(train.engines.0=… 这种)

2. 直接复用 pydantic 的校验/默认值/alias,不用再维护两套规则

一个小建议:后面如果有空,可以补一节“和 hydra/tyro/argparse 的对比表”,比如学习成本、嵌套支持、静态类型友好度、配置文件 round-trip(读入+导出)这些维度。这样新用户会更快判断适配场景。

总之很不错,已 star~

看起来挺实用的
.0是不是让我想起了什么(
给我干哪来了,这还是python吗((