跳转到内容
新建笔记

Python argparse:命令行参数、显式输入与解析边界

argparse 把一组命令行字符串转换为命名参数,并负责帮助、类型转换和常见错误提示。先声明接口,再解析输入;它不会自动执行命令、打开数据文件或验证模型是否支持某种精度。

本页以 Python 3.11 为验证基线。原记录中的名称、输入路径、FP16 开关、显式参数列表和剩余配置项都保留;混入代码块的下载网址、说明文字和未拆分的命令字符串不属于可执行 Python。

一个能够直接运行的入口

跳转到“一个能够直接运行的入口”

保存为 demo_args.py。把构造解析器与执行入口分开,测试或 Notebook 就能直接传入参数列表。

import argparse
from pathlib import Path
def positive_int(text: str) -> int:
try:
value = int(text)
except ValueError as exc:
raise argparse.ArgumentTypeError("必须是正整数") from exc
if value <= 0:
raise argparse.ArgumentTypeError("必须是正整数")
return value
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="演示输入路径、批大小和精度选项",
allow_abbrev=False,
)
parser.add_argument("-n", "--name", default="demo", help="本次运行名称")
parser.add_argument("--input", type=Path, default=Path("input.txt"))
parser.add_argument("--addresses", default="localhost")
parser.add_argument("-b", "--batch-size", type=positive_int, default=1)
parser.add_argument("--mode", choices=("train", "demo"), default="demo")
parser.add_argument("-f", "--fp16", action="store_true",
help="请求使用 FP16;实际支持由业务层检查")
return parser
def main(argv=None) -> int:
args = build_parser().parse_args(argv)
print(f"name={args.name} input={args.input} batch={args.batch_size} "
f"mode={args.mode} fp16={args.fp16} addresses={args.addresses}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

在终端中调用:

python demo_args.py --help
python demo_args.py -n experiment -b 4 --input "data/input one.txt" -f
python demo_args.py --mode train --addresses localhost

这里 -n 与 --name 是同一个参数的两个名称。短选项由声明决定,并不是任意长选项都自动获得首字母别名。--batch-size 默认保存到 args.batch_size。type=Path 只构造路径对象,不检查存在性;输入是否是文件、能否读取、数据格式是否正确,仍属于业务校验。

声明没有提供时提供时
default="demo"使用字符串默认值普通选项读取下一个字符串
type=positive_int本例默认值已经是整数转换并拒绝 0、负数及非整数
choices=("train", "demo")使用声明的默认值只接受列出的两个值
action="store_true"False出现开关后为 True,不再取一个布尔值
required=True 的选项缺失即报错必须显式提供;有 default 也不能代替它

不要用 type=bool 解析 "False":非空字符串转换为布尔值仍为真。--fp16 False 也不是关闭开关的写法,这里的 False 会成为多余参数。需要成对的 --feature/--no-feature 时,可使用 Python 3.9 起提供的 BooleanOptionalAction。

参数列表与进程参数不同

跳转到“参数列表与进程参数不同”
调用方式解析的数据
parser.parse_args() 或 parse_args(None)当前进程的 sys.argv[1:]
parser.parse_args(["-n", "trial", "-b", "2"])传入的四个字符串,不包含程序名
parser.parse_args([])空参数列表;仍会检查必需选项和位置参数
parser.parse_args(["--input", "a b.txt"])文件名是一个含空格的参数,不包含额外引号字符

命令行引号由外层终端处理。直接传列表时,每一项已经是一个参数,不能把 "--mode demo" 放在同一个元素中,也不能把中文逗号当 Python 列表分隔符。像 -b2 这样的短选项和值连写,在该选项接受一个值时通常可以解析;清晰的列表形式更便于测试。

下面独立示例适合 Notebook;它不会接收内核自动注入的 -f ...json 参数,也不修改全局 sys.argv。

import argparse
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument("--type", choices=("demo", "train"), default="demo")
parser.add_argument("--cfg_file", default="configs/linemod.yaml")
parser.add_argument("demo_path", nargs="?", default="demo_images/cat")
args = parser.parse_args([
"--type", "demo",
"--cfg_file", "configs/linemod.yaml",
"demo_images/cat",
])
assert args.type == "demo"
assert args.demo_path == "demo_images/cat"
print(args)

原记录先改 sys.argv 再导入配置模块,这依赖模块在导入时解析参数,还会受导入缓存影响。可维护的接口应接受显式参数列表或配置对象;不能靠重复导入保证配置重新生效。若只处理已有第三方接口,也应先明确它是否在导入时有副作用。

剩余配置项要有明确边界

跳转到“剩余配置项要有明确边界”

argparse.REMAINDER 将位置参数开始后的剩余字符串保留为列表,供下一层解析。它本身不解释 KEY VALUE 的含义,也不检查数量是否为偶数。没有剩余项时,本例得到 [];不能因写过 default=None 就假定结果必定为 None。

import argparse
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument("--cfg", default="config.yaml")
parser.add_argument("opts", nargs=argparse.REMAINDER)
args = parser.parse_args(["--cfg", "demo.yaml", "MODEL.NAME", "tiny", "--fp16"])
assert args.cfg == "demo.yaml"
assert args.opts == ["MODEL.NAME", "tiny", "--fp16"]
assert parser.parse_args([]).opts == []
separated = parser.parse_args(["--", "--threshold", "0.5"])
assert separated.opts == ["--", "--threshold", "0.5"]
forwarded = separated.opts[1:] # 本接口明确约定移除这一个分隔标记
assert forwarded == ["--threshold", "0.5"]
print(args.opts)

上例中 MODEL.NAME 启动了剩余位置参数,后面的 --fp16 就不再是外层开关。只有剩余部分、且第一个词本身以 -- 开头时,用明确分隔标记并约定下一层怎样处理;不要依赖未声明选项被偶然接收。

parse_known_args() 返回 (已知参数, 剩余字符串),适合确实有下一层负责处理的接口;不能把剩余项直接丢掉,否则拼错选项也会被掩盖。关闭 allow_abbrev 可防止唯一前缀被当作长选项缩写,但它不是完整的业务校验。Python argparse 文档

默认情况下,帮助会触发 SystemExit(0),解析错误会向标准错误输出提示并触发 SystemExit(2)。这适合命令行程序;库函数和 Notebook 应自行设计错误接口。Python 3.11 的 exit_on_error=False 并不保证所有解析错误都变成 ArgumentError,例如某些必需参数或未知参数错误仍可能退出,不能把它当成全面禁止退出的开关。

解析函数的测试应传列表,检查得到的值以及失败输入。执行程序的测试还应检查真实退出码和标准输出/错误,不能只测 Namespace 对象。本页示例只解析参数与打印结果,不读取模型、不运行推理,也不会因为 fp16=True 就证明硬件支持 FP16。

原来的 pythonw -m imagepy 是 Windows 下用无控制台解释器启动模块的例子,并非 argparse 语法。排查导入或参数错误时先用有控制台的 python -m imagepy 查看信息,且需先在同一解释器环境中安装对应包;本页验证未安装或启动 ImagePy。解释器与进程参数见 sys,路径和子进程见 os。