argparse:别再手写 sys.argv 解析了
用标准库 argparse 声明命令行参数:自动帮助、类型与 choice 校验;别用 type=bool,开关用 store_true 或 BooleanOptionalAction。
要解决的问题
写 Python 后端或运维脚本时,总会碰到命令行入口:备份目录、跑批任务、起本地调试服务。很多人第一反应是直接抠 sys.argv[1],再自己写一堆 if 判断 -v、--output。结果是:缺参时没有友好报错、--help 要手写、类型转换(字符串变 int / Path)散落各处,测试时还得伪造整个 sys.argv。
标准库 argparse(自 Python 3.2)就是干这件事的:声明「需要哪些参数」,它负责从 sys.argv 里解析、自动生成帮助、发现非法参数时报错退出。本文示例已在本机 Python 3.13.5 跑通;核心 API 在 3.9+ 一致(文中会标出 3.9 / 3.13 才有的点)。
核心概念
以 Python 3.13 argparse 文档 为准,先抓住这几条:
ArgumentParser:整份命令行契约的容器。可设prog、description、epilog;默认自带-h/--help。add_argument(...):声明位置参数(如src)或可选参数(如-o/--output)。常用关键字:type(转换)、choices(枚举)、default、required、help、nargs、action。parse_args():解析后返回argparse.Namespace,用属性读值(args.verbose)。可传入字符串列表,方便单测,不必改全局sys.argv。action:常见有store(默认)、store_true/store_false、append、count。3.9+ 还有BooleanOptionalAction,一次声明同时得到--feature与--no-feature。- 非法参数默认退出:
parse_args遇到类型错误、非法 choice、缺必填项时,打印 usage 并以状态码 2 退出。若要自己捕获,可用exit_on_error=False(3.9+) 再接ArgumentError。
文档也写明:argparse 是标准库里做基础命令行应用的推荐默认;若你需要更极端的控制(例如完全禁止选项与位置参数交错),再去对照官方「Choosing an argument parsing library」。
示例代码
一个「备份」小入口:位置参数 + 必填输出路径 + 开关 + 带默认值的整数 + 枚举(本地已跑通):
import argparse
from pathlib import Path
parser = argparse.ArgumentParser(
prog="backup",
description="把目录打包成备份文件",
)
parser.add_argument("src", type=Path, help="源目录")
parser.add_argument("-o", "--output", type=Path, required=True, help="输出路径")
parser.add_argument("-v", "--verbose", action="store_true", help="打印细节")
parser.add_argument(
"--keep",
type=int,
default=7,
metavar="N",
help="保留最近 N 份备份(默认:%(default)s)",
)
parser.add_argument(
"--level",
choices=["debug", "info", "warning"],
default="info",
)
# 单测或 REPL 里传入列表,不必改 sys.argv
args = parser.parse_args(
["/tmp/data", "-o", "/tmp/out.tgz", "-v", "--keep", "3"]
)
assert args.src == Path("/tmp/data")
assert args.output == Path("/tmp/out.tgz")
assert args.verbose is True
assert args.keep == 3
assert args.level == "info"
# print(parser.format_help()) # 查看自动生成的帮助
type=Path、type=int 会在解析阶段转换;非法值(如 --keep abc)会立刻报错退出,不用业务代码再 try/int。help 里的 %(default)s 会填入默认值,和文档说明一致。
需要「同一选项出现多次」或「至少一个文件」时:
import argparse
p = argparse.ArgumentParser()
p.add_argument("--tag", action="append", default=[])
p.add_argument("files", nargs="+")
args = p.parse_args(["--tag", "a", "--tag", "b", "f1", "f2"])
assert args.tag == ["a", "b"]
assert args.files == ["f1", "f2"]
action="append" 每次追加到列表;nargs="+" 表示至少一个位置参数,缺了会报错。
布尔开关若希望正反两种写法(3.9+):
import argparse
p = argparse.ArgumentParser()
p.add_argument("--feature", action=argparse.BooleanOptionalAction, default=False)
assert p.parse_args(["--feature"]).feature is True
assert p.parse_args(["--no-feature"]).feature is False
常见误区
-
用
type=bool解析布尔
官方文档明确不推荐:bool("False")在 Python 里是True(非空字符串)。结果是命令行写了False,程序仍当成开。请用action="store_true",或 3.9+ 的BooleanOptionalAction。 -
把本该是位置参数的东西硬做成
required=True的可选参数
文档写:required 选项通常被视为不良形式,因为用户默认认为「选项」可以省略。能写成位置参数就写位置参数;确需可选形式时再required=True。 -
继续手拆
sys.argv,却又混用argparse
解析交给parse_args;业务只读Namespace。测试时传字符串列表即可。半自动半手写,边界情况(--、缩写、负数字符串)更容易踩坑。 -
在
type=里做「打开文件 / 读 JSON」等重活
文档提醒:type适合简单转换(int、float、Path)。复杂错误(JSON 解码、文件找不到)用type往往报错不清晰,甚至像FileType在后续参数失败时可能来不及关掉已打开的文件。更稳妥是先解析出路径/字符串,再在下游用with打开、再json.loads。 -
忘记非法参数会
sys.exit(2)
默认exit_on_error=True。若在库代码或需要自定义错误处理的场景调用parse_args,要么包一层、要么设exit_on_error=False(3.9+)自行捕获argparse.ArgumentError。
小结
后端脚本、管理命令、本地工具入口,优先用标准库 argparse:声明参数、自动帮助、类型与 choice 校验,测试时传入列表即可。开关用 store_true / BooleanOptionalAction(3.9+),别用 type=bool;列表用 append / nargs;重逻辑放在解析之后。正式项目以你当前解释器版本的 argparse 文档 为准。
参考资料
谢谢你读到这里。