Python——ArgumentParser类的使用


整体说明

  • 在 Python 里,argparse.ArgumentParser() 是标准库 argparse 模块的核心部分,其主要功能是解析命令行参数
  • 大部分优秀的开源函数都会使用这个功能

代码示例

  • ArgumentParser类使用示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    import argparse

    def main():
    # 创建 ArgumentParser 对象
    parser = argparse.ArgumentParser(
    description='这是一个命令行工具演示程序',
    epilog='示例: python demo.py -n Alice -a 35 --verbose'
    )

    # 添加参数
    # 位置参数 (未添加-为前缀的是位置参数,位置参数是必须给出的)
    parser.add_argument('input_file', help='输入文件路径')

    # 可选参数(添加-或--的是可选参数),同一行可以同时注册-或--,都可以作为输入参数
    # # 但代码内部名字命名规则是--优先-,没有--时,代码内部-才会生效(比如下面的定义下,parser.output 和 parser.x 生效,但是 parser.o 会报错)
    parser.add_argument('-o', '--output', default='output/result.txt', help='输出文件路径') # 调用时使用args.output调用参数
    parser.add_argument('-x', help='x') # 调用时使用args.x调用参数
    parser.add_argument('-n', '--name', default='Guest', help='用户名称') # 调用时使用args.x调用参数
    parser.add_argument('--age', type=int, choices=range(30, 40), help='用户年龄(30-39)')
    # action='store_true'表示这是布尔变量,若传入命令行存在该参数,则该 Python 参数值为 true,否则为 false;'store_false' 则功能相反
    parser.add_argument('--verbose', action='store_true', help='显示详细输出')
    parser.add_argument('--color', choices=['red', 'green', 'blue'], default='blue', help='颜色选择')
    parser.add_argument('--scale', type=float, default=0, help='缩放比例')

    # 互斥参数组,同时指定会出错
    group = parser.add_mutually_exclusive_group()
    group.add_argument('--fast', action='store_true', help='快速模式')
    group.add_argument('--slow', action='store_true', help='慢速模式')

    # 以上都是自动推导 dest,下面显示定义 dest(注意:dest 是真正在 args 中可以被使用的参数名称)
    parser.add_argument("--use_flash_attn", action="store_true", dest="use_flash_attn", default=False)
    ## 而且可以考虑将同一个 dest 绑定多个输入参数,比如下面的句子使得可以同时通过 `--use_flash_attn` 和 `--no_use_flash_attn` 控制同一个参数:
    parser.add_argument("--no_use_flash_attn", action="store_false", dest="use_flash_attn")
    ## dest 可以和原始的参数名完全无关(下面示例中 dest 与参数名不同,访问时要通过 args.enable_attention 而不是 args.use_attention)
    parser.add_argument("--use_attention", action="store_true", dest="enable_attention", default=False)

    # * 解析参数
    args = parser.parse_args()

    print("args对象完整属性:", args.__dict__)
    print("--end--\n")
    # * 使用参数
    print(f"输入文件: {args.input_file}")
    if args.output:
    print(f"输出文件: {args.output}")
    print(f"用户名: {args.name}")
    if args.age:
    print(f"年龄: {args.age}")
    print(f"详细模式: {'是' if args.verbose else '否'}")
    print(f"颜色: {args.color}")
    print(f"缩放比例: {args.scale}")
    if args.fast:
    print("模式: 快速")
    elif args.slow:
    print("模式: 慢速")
    else:
    print("模式: 默认")

    # 根据参数值给出提示
    if args.use_flash_attn:
    print("FlashAttention 已启用")
    else:
    print("FlashAttention 未启用")

    if args.enable_attention:
    print("Attention 已启用")
    else:
    print("Attention 未启用")

    if __name__ == '__main__':
    main()

    # args对象完整属性: {'input_file': 'text.txt', 'output': 'output/result.txt', 'x': None, 'name': 'Guest', 'age': 33, 'verbose': False, 'color': 'blue', 'scale': 0, 'fast': False, 'slow': False, 'use_flash_attn': False, 'enable_an': False}
    # --end--
    #
    # 输入文件: text.txt
    # 输出文件: output/result.txt
    # 用户名: Guest
    # 年龄: 33
    # 详细模式: 否
    # 颜色: blue
    # 缩放比例: 0
    # 模式: 默认
    # FlashAttention 未启用
    # Attention 未启用

Python 内部变量参数命名规则

  • --- 同时存在时,优先 -- 定义的变量
    • 注:传入时都可以使用,且没有优先级,只会用后面的覆盖前面的
  • --output-path 等会自动被转换成 parser.output_path 变量访问
    • 注:传入时还是必须跟参数定义方式对齐 --output-path
    • 当然:--output_path 也是相同的参数名 parser.output_path ,若同时定义了 --output_path--output-path,则两者实际上是同一个参数,且互相之间可以被覆盖

ArgumentParser 类其他使用注意事项

  • 参数类型
    • 默认情况下参数是字符串类型,可以使用 type 指定其他类型
    • 常用类型:int, float, str (默认)
  • 参数种类
    • 位置参数:没有前缀,必须提供
    • 可选参数:以 --- 开头,可选提供
    • 注:位置参数是必须的,可选参数可通过增加 required=True 来设定为必须参数
  • 常用参数选项
    • help:参数的帮助信息
    • default:参数的默认值
    • choices:限制参数的取值范围
    • action:参数的动作,常见值:
      • store:存储参数值(默认)
      • store_true/store_false:存储布尔值
      • append:允许多次使用参数并存储为列表
  • 互斥参数
    • 使用 add_mutually_exclusive_group() 创建互斥组
    • 组内的参数不能同时使用
  • 帮助信息
    • description:程序的简要描述
    • epilog:帮助信息末尾的文本
    • 自动生成的帮助信息可以通过 -h--help 查看
  • 错误处理
    • 当用户输入无效参数时,argparse 会自动生成错误信息并退出,会抛出 ArgumentErrorSystemExit 异常
  • 子命令
    • 对于复杂的命令行工具,可以使用 add_subparsers() 实现子命令功能
    • 类似 gitcommit, push 等子命令
  • 参数前缀
    • 通常单字母参数用 - 前缀,完整单词用 -- 前缀
    • 但这不是强制要求,只是约定俗成

关于 dest 的特殊说明

  • dest 决定了解析结果存储在 args 对象中的变量名 ,即通过 args.xxx 访问时的 xxx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    parser.add_argument(
    "--use_flash_attn",
    action="store_true",
    dest="enable_flash", # dest 与参数名不同
    default=False,
    )

    args = parser.parse_args()
    args.enable_flash # 正确访问方式
    args.use_flash_attn # AttributeError,dest 才是真正的变量名
  • 理解:destargs.xxx 里的 xxx,命令行参数名只是触发器,真正存数据的是 dest

常见方式:自动推导规则(不写 dest

  • 自动推导 dest 示例:
    1
    2
    3
    4
    5
    # 规则:去掉 -- 前缀,- 替换为 _
    parser.add_argument("--use_flash_attn") # dest → "use_flash_attn"
    parser.add_argument("--learning-rate") # dest → "learning_rate"
    parser.add_argument("-v") # dest → "v"
    parser.add_argument("--no_use_flash_attn") # dest → "no_use_flash_attn" ⚠️ 不是 use_flash_attn!

必须显式指定 dest 的场景

  • 场景1:解决短选项名语义不清

    1
    2
    parser.add_argument("-v", dest="verbose")
    # 否则只能用 args.v 访问,语义不明确
  • 场景2:多个参数映射到同一变量(最典型!)

    1
    2
    3
    4
    5
    6
    7
    # 不指定 dest → 两个参数完全独立,互不影响
    parser.add_argument("--use_flash_attn", action="store_true") # dest → use_flash_attn
    parser.add_argument("--no_use_flash_attn", action="store_false") # dest → no_use_flash_attn

    # 指定 dest → 两个参数绑定同一变量,形成开关联动
    parser.add_argument("--use_flash_attn", action="store_true", dest="use_flash_attn", default=False)
    parser.add_argument("--no_use_flash_attn", action="store_false", dest="use_flash_attn")
    • 执行结果:
      1
      2
      3
      $ python test.py                        # Namespace(use_flash_attn=False)
      $ python test.py --use_flash_attn # Namespace(use_flash_attn=True)
      $ python test.py --no_use_flash_attn # Namespace(use_flash_attn=False)
  • 场景3:参数名不是合法的变量名

    1
    2
    parser.add_argument("--2d-output", dest="output_2d")
    # --2d-output 不能直接作为变量名(数字开头),用 dest 自定义

两个参数绑定到同一个目标参数上的实现

  • 背景:有时候我们想要增加可读性,对于 布尔类型的变量可能会定义两个输入方式
    • 比如同一个参数:添加 --use_flash_attn 表示 True,添加 --no_use_flash_attn 时表示 False

两个参数绑定到同一个目标参数上

  • 示例:用 argparse 实现布尔开关 ,两个参数共享同一个 dest

    1
    2
    parser.add_argument("--use_flash_attn",    action="store_true",  dest="use_flash_attn", default=False)
    parser.add_argument("--no_use_flash_attn", action="store_false", dest="use_flash_attn")
    • 注:之前我们一般不使用 dest 参数,是因为 dest 会被自动推导,这里为了实现将同一个变量绑定到两个参数,需显示指定 dest
  • 不同传参下的运行结果:

    命令行输入 args.use_flash_attn
    python script.py False(走 default)
    python script.py --use_flash_attn True
    python script.py --no_use_flash_attn False
    python script.py --use_flash_attn --no_use_flash_attn False(后者覆盖前者)

补充:其他更推荐的写法(Python 3.9+)

  • 使用 argparse.BooleanOptionalAction,更简洁:

    1
    2
    3
    4
    5
    6
    parser.add_argument(
    "--use_flash_attn",
    action=argparse.BooleanOptionalAction, # 自动生成 --no_use_flash_attn
    default=False,
    help="Enable/Disable FlashAttention"
    )
  • 两种写法效果完全等价,都支持 --use_flash_attn--no-use_flash_attn 两种形式