1. 最小CLI骨架与参数解析

最小CLI骨架与参数解析 学习卡片

为什么从“最小骨架”开始

一个可用的 CLI 程序,最小闭环只需要三件事:接收参数 → 解析参数 → 根据参数执行动作。Python 标准库的 argparse 已经把这套机制封装好了,我们不需要引入任何第三方依赖,就能得到一个带帮助信息、错误提示和退出码的可执行程序。

本例的目标是写出一个 greet 命令:它接受一个位置参数 name,以及一个可选参数 --times(默认 1 次),并支持 -h/--help 自动输出帮助信息。

参数解析的职责边界

在动手写代码之前,先看清 argparse 在程序中的位置。它不负责业务逻辑,只负责把命令行字符串翻译成 Python 对象,并处理“用户输入不合法”时的报错与退出。

CLI 参数解析数据流 命令行输入 greet Alice --times 3 ArgumentParser add_argument() parse_args() -h / --help 自动生成 Namespace name='Alice' times=3 业务逻辑 print(name * times) 错误路径 缺少参数 → 退出码 2 argparse 负责“翻译 + 校验”,业务逻辑只消费 Namespace 对象

完整可运行代码

下面这段代码可以直接保存为 greet.py 并运行。它不依赖任何第三方库,Python 3.6+ 均可执行。

#!/usr/bin/env python3
"""最小 CLI 骨架:位置参数 + 可选参数 + 自动帮助。"""

import argparse
import sys

def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="greet",
        description="向指定名字打招呼,可重复多次。",
        epilog="示例:greet Alice --times 3",
    )
    # 位置参数:必须提供
    parser.add_argument(
        "name",
        help="要打招呼的名字",
    )
    # 可选参数:有默认值
    parser.add_argument(
        "-t", "--times",
        type=int,
        default=1,
        help="重复次数(默认 1)",
    )
    return parser

def main(argv=None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    if args.times < 1:
        parser.error("--times 必须大于等于 1")

    for _ in range(args.times):
        print(f"Hello, {args.name}!")

    return 0

if __name__ == "__main__":
    sys.exit(main())

运行与验证

把文件保存后,依次执行以下命令,观察输出是否符合预期:

命令 预期输出 退出码
python greet.py Alice Hello, Alice! 0
python greet.py Alice --times 3 三行 Hello, Alice! 0
python greet.py -h 打印 usage、位置参数、可选参数说明 0
python greet.py 报错:the following arguments are required: name 2
python greet.py Alice --times 0 报错:--times 必须大于等于 1 2
验证要点:退出码是 CLI 程序契约的一部分。正常执行返回 0,参数错误返回 2(argparse 默认行为),业务校验失败也返回 2。这样脚本可以被其他程序可靠地调用和判断。

关键设计点

  • prog 参数:显式指定程序名,避免帮助信息里出现 greet.py 这种带扩展名的路径。
  • type=int:让 argparse 在解析阶段就完成类型转换,非法输入(如 --times abc)会自动报错,业务代码不需要再做字符串转换。
  • default=1:可选参数不传时使用默认值,保证 args.times 始终是整数。
  • parser.error():业务层校验失败时调用它,会打印错误信息并以退出码 2 结束,保持与参数错误一致的对外行为。
  • main(argv=None):把参数列表作为可选入参,方便后续写单元测试时直接传入 ["Alice", "--times", "2"],而不必真的启动子进程。
最小骨架的边界:本例只处理“单命令 + 位置参数 + 可选参数”。当需要 greet add、greet list 这类多命令结构时,就需要引入子命令路由,那是下一个案例要解决的问题。