• argparse——命令行参数解析


    一、argparse是什么?

    argparse 是 Python 的一个标准库,用于命令行参数的解析,这意味着我们无需在代码中手动为变量赋值,而是可以直接在命令行中向程序传递相应的参数,再由变量去读取这些参数。

    如果没有安装 argparse,可执行如下命令进行安装

    pip install argparse
    
    • 1

    使用时只需导入

    import argparse
    
    • 1

    1.1 一个🌰

    我们先来看一个最简单的例子,了解了使用 argparse 的大致步骤后,再来详细介绍各个API。

    """ 求解两数之和 """
    twoSum = lambda x, y: x + y
    
    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int, required=True, help="first number")
    parser.add_argument('--b', type=int, required=True, help="second number")
    args = parser.parse_args()
    
    first_num, second_num = args.a, args.b
    print(twoSum(first_num, second_num))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    将以上内容保存到 demo.py 中。我们先在命令行执行

    python3 demo.py -h
    
    • 1

    可以看到相应的帮助信息

    usage: demo.py [-h] --a A --b B
    
    options:
      -h, --help  show this help message and exit
      --a A       first number
      --b B       second number
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    usage 可以看出 demo.py 接收两个必选项:--a--b(带有 [] 的为可选项,没带的为必选项),它们分别代表第一个数和第二个数,其中 AB 分别代表实际传入的参数。

    在命令行中执行

    python3 demo.py --a 12 --b 19
    
    • 1

    返回结果是 31,这说明我们通过命令行完成了求解两数之和这个操作。

    二、ArgumentParser

    使用 argparse 的第一步是先创建一个 ArgumentParser 对象,该对象包含将命令行解析成 Python 数据类型所需的全部信息,其常用参数如下

    argparse.ArgumentParser(prog=None, usage=None, description=None, epilog=None)
    
    • 1

    2.1 prog

    prog 默认值为 os.path.basename(sys.argv[0]),也就是程序所在文件的名称。例如在第一章节中,我们在创建 ArgumentParser 对象时没有指定 prog,因此采用了默认值 demo.py

    先来看一个例子

    parser = argparse.ArgumentParser()
    parser.print_help()  # 和在命令行调用 python3 demo.py -h 的效果一样(会有一些细微差别)
    
    • 1
    • 2

    输出的帮助信息:

    usage: demo.py [-h]
    
    options:
      -h, --help  show this help message and exit
    
    • 1
    • 2
    • 3
    • 4

    可以看到若不指定 prog,则帮助信息将显示 demo.py 作为程序名称。现在指定 prog

    parser = argparse.ArgumentParser(prog="My Program")
    
    • 1

    相应的帮助信息:

    usage: My Program [-h]
    
    options:
      -h, --help  show this help message and exit
    
    • 1
    • 2
    • 3
    • 4

    可以看到原先 demo.py 的地方变成了 My Program

    2.2 usage

    默认情况下,ArgumentParser 根据它包含的选项来构建用法消息。

    这里依然使用第一章节的例子:

    usage: demo.py [-h] --a A --b B
    
    options:
      -h, --help  show this help message and exit
      --a A       first number
      --b B       second number
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    因为我们没有指定 usage,所以 ArgumentParser 将使用它包含的三个选项:-h--a--b 来构建用法消息 usage,它位于帮助信息的第一行。

    如果觉得默认的 usage 有些冗余(因为下方的 options 已经详细介绍了各个选项),我们可以自定义 usage

    parser = argparse.ArgumentParser(usage="See the options below for usage")
    
    • 1

    相应的帮助信息变成

    usage: See the options below for usage
    
    options:
      -h, --help  show this help message and exit
      --a A       first number
      --b B       second number
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    需要注意的是,在指定了 usage 后,prog 将会被覆盖,即使在 ArgumentParser 中指定了 prog 也没有用。

    2.3 description

    description 参数用来简要描述这个程序做什么以及怎么做。不指定 description 时,帮助信息中将不予显示。

    依然使用第一章节的例子,这里我们指定 description

    parser = argparse.ArgumentParser(description="This is my program.")
    
    • 1

    相应的帮助信息

    usage: demo.py [-h] --a A --b B
    
    This is my program.
    
    options:
      -h, --help  show this help message and exit
      --a A       first number
      --b B       second number
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看到 usageoptions 中间多了一行内容,这就是我们指定的 description

    2.4 epilog

    该参数和 description 类似,区别在于,description 放在了 options 之前,而 epilog 放在了 options 之后。

    依然使用第一章节的例子,这里我们同时指定 descriptionepilog

    parser = argparse.ArgumentParser(description="This is my program.", epilog="The end.")
    
    • 1

    相应的帮助信息

    usage: demo.py [-h] --a A --b B
    
    This is my program.
    
    options:
      -h, --help  show this help message and exit
      --a A       first number
      --b B       second number
    
    The end.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    📝 通常来讲,以上四个参数中用的最多的是 prog

    三、add_argument

    add_argument() 方法用于向解析器中添加一个选项(位置参数)。

    ArgumentParser.add_argument(
    	name or flags...,
    	nargs,
    	default,
    	type,
    	choices,
    	required,
    	help
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以上仅列出了 add_argument() 方法中最常用的几个参数。

    3.1 name or flags

    name or flags选项options)或位置参数positional arguments)。如果是选项的话可以传入一系列flags(例如自带的帮助就有两个:-h--help),如果是位置参数的话则只能传入一个 name

    例如

    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--install')
    args = parser.parse_args()
    
    • 1
    • 2
    • 3

    相应的帮助信息:

    usage: demo.py [-h] [-i INSTALL]
    
    options:
      -h, --help            show this help message and exit
      -i INSTALL, --install INSTALL
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这意味着我们在命令行调用 python3 demo.py -i INSTALLpython3 demo.py --install INSTALL 是等价的。

    与选项不同的是,位置参数前不能加 -,并且每次只能传入一个,例如

    parser = argparse.ArgumentParser()
    parser.add_argument('param1')
    parser.add_argument('param2')
    args = parser.parse_args()
    
    • 1
    • 2
    • 3
    • 4

    相应的帮助信息:

    usage: demo.py [-h] param1 param2
    
    positional arguments:
      integer
    
    options:
      -h, --help  show this help message and exit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    usage 可以看出位置参数在调用命令行时是必须传入的。

    以上是先添加位置参数 param1 再添加 param2 的,如果我们调换顺序,则帮助信息中的两个参数的位置也将调换,这也诠释了 “位置” 的含义。

    📝 可以看出,选项和位置参数,前者相当于关键字传参,后者相当于位置传参。

    3.2 type & default

    顾名思义,type 指选项或位置参数将要被转换成的数据类型(在命令行中传入的参数都默认以 str 类型存在)。

    例如

    parser = argparse.ArgumentParser()
    parser.add_argument('--a')
    parser.add_argument('--b', type=int)
    args = parser.parse_args()
    print(type(args.a), type(args.b))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行 python3 demo.py --a 3 --b 3 后得到结果

    <class 'str'> <class 'int'>
    
    • 1

    default 指选项或位置参数的默认值,例如

    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int, default=5)
    args = parser.parse_args()
    print(args.a)
    
    • 1
    • 2
    • 3
    • 4

    直接执行 python3 demo.py 将会输出 5,因为采用了默认值。如果执行 python3 demo.py --a x 则会输出 xx 是任何整数,且不能省略)。

    ⚠️ 如果没有为 --a 指定默认值,且在命令行执行时也没有向 --a 传参,则 args.aNone

    3.3 required & help

    因为位置参数在命令行中是必须传入的,所以 required 只能用于选项。required 设为 True 则代表此选项为必选项,否则为可选项,默认为 False

    例如

    parser = argparse.ArgumentParser()
    parser.add_argument('--a')
    args = parser.parse_args()
    
    • 1
    • 2
    • 3

    此时帮助信息为

    usage: demo.py [-h] [--a A]
    
    options:
      -h, --help  show this help message and exit
      --a A
    
    • 1
    • 2
    • 3
    • 4
    • 5

    usage 一行中 --a A 被一对方括号 [] 括了起来,说明 --a 是可选项。现在指定 required=True

    parser.add_argument('--a', required=True)
    
    • 1

    这时候帮助信息变为

    usage: demo.py [-h] --a A
    
    options:
      -h, --help  show this help message and exit
      --a A
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到 [] 消失了,说明 --a 变成了必选项。

    help 用来描述一个选项或位置参数,该描述将会显示在帮助信息中

    parser = argparse.ArgumentParser()
    parser.add_argument('--lr', type=float, default=1e-3, help="learning rate")
    args = parser.parse_args()
    
    • 1
    • 2
    • 3

    相应的帮助信息

    usage: demo.py [-h] [--lr LR]
    
    options:
      -h, --help  show this help message and exit
      --lr LR     learning rate
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.4 nargs & choices

    假如选项 --a 需要接收5个参数,此时需要用 nargs 来指定

    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int, nargs=5)
    args = parser.parse_args()
    print(args.a)
    
    • 1
    • 2
    • 3
    • 4

    执行 python3 demo.py --a 1 2 3 4 5 可以得到

    [1, 2, 3, 4, 5]
    
    • 1

    需要注意,nargs=1 最终会得到一个只含一个元素的列表,而非元素本身。

    更进一步,nargs='?' 代表传入参数的数量为0个或1个,nargs='+' 代表传入参数的数量至少1个,nargs='*' 代表可传入任意多的参数。

    有些时候,选项 --a 的取值只能是固定的几种,例如 --a 只能从整数1,3,5中选取,这时候需要用 choices 来指定一个列表

    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int, choices=[1, 3, 5])
    args = parser.parse_args()
    
    • 1
    • 2
    • 3

    相应的帮助信息

    usage: demo.py [-h] [--a {1,3,5}]
    
    options:
      -h, --help   show this help message and exit
      --a {1,3,5}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果 --a 后面跟的数字不是1,3,5中的一个就会报错。

    四、parse_args

    我们先来看一个例子。

    import argparse
    import sys
    
    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int)
    parser.add_argument('--b', type=int)
    print(sys.argv)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在命令行执行 python3 demo.py --a 3 --b 5 得到结果

    ['demo.py', '--a', '3', '--b', '5']
    
    • 1

    从中可以看出,sys.argv[0] 是文件名,sys.argv[1:] 是我们在命令行中传入的选项。

    在之前的学习过程中,可能你已经注意到了,每次我们为解析器添加完相应的选项/位置参数后,都要执行一遍 parser.parse_args()。默认情况下,parse_args() 采用 sys.argv[1:] 作为其参数,并返回一个命名空间(类似于字典)。

    举个🌰

    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int, nargs=3)
    parser.add_argument('--b', type=str)
    parser.add_argument('--c', type=float)
    args = parser.parse_args()
    print(type(args))
    print(args)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行 python3 demo.py --a 1 3 5 --b 'k' --c 3.14 得到

    <class 'argparse.Namespace'>
    Namespace(a=[1, 3, 5], b='k', c=3.14)
    
    • 1
    • 2

    如果只执行 python3 demo.py --a 1 3 5 --b 'k',则得到

    <class 'argparse.Namespace'>
    Namespace(a=[1, 3, 5], b='k', c=None)
    
    • 1
    • 2

    可以看出,如果在命令行中没有提供相应的选项,并且该选项也没有默认值,则在命名空间中该选项的值为 None,这一点我们早在 3.2 节中就已经提到了。

    parser.parse_args() 返回的是一个命名空间对象,我们通常用 args 来存储。要访问 args 中键 k 对应的值 v,只需要 args.k 即可。

    五、避免报错

    在执行 args = parser.parse_args() 这一步中,可能会出现报错情况,例如

    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=int)
    args = parser.parse_args()
    
    • 1
    • 2
    • 3

    如果我们在命令行执行 python3 demo.py --a 'abc' 就会报错,这是因为字符串无法转换成整数。

    usage: demo.py [-h] [--a A]
    demo.py: error: argument --a: invalid int value: 'abc'
    
    • 1
    • 2

    一般我们会采用如下代码块来避免直接看到不友好的报错

    try:
        args = parser.parse_args()
    except:
        parser.print_help()
        sys.exit(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    六、使用shell脚本进行调参

    深度学习经常需要调参,如果直接使用IDE打开 .py 文件去调未免显得有些笨拙,而且也会变得不好维护。如果使用 argparse,虽然不用每次修改 .py 文件,但在命令行里反复修改也略显麻烦,这时候就需要将其与shell脚本进行结合了。

    为简便起见,假设我们的项目架构如下:

    myproject
    ├── __init__.py
    ├── model
    │   ├── model1.py
    │   ├── model2.py
    │   └── model3.py
    ├── scripts
    │   └── train.sh
    ├── train.py
    └── utils
        ├── utils1.py
        └── utils2.py
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其中 train.py 中的内容为

    import argparse
    import sys
    # 导入其他的包...
    
    # 假设只有两个超参数需要调
    parser = argparse.ArgumentParser()
    parser.add_argument('--bs', type=int, default=128, help="batch size")
    parser.add_argument('--lr', type=float, default=0.001, help="learning rate")
    
    try:
        args = parser.parse_args()
    except:
        parser.print_help()
        sys.exit(0)
    
    # 超参数设置
    BATCH_SIZE = args.bs
    LEARNING_RATE = args.lr
    
    # 其他代码...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这时候我们只需要在脚本目录 scripts 下新建一个文件 train.sh,向其中写入内容

    # 用来确保无论在哪里执行该脚本,都能够正确返回该脚本所在的目录,以便后续根据这个目录来定位所要运行程序的相对位置
    cd "$(dirname $0)"
    
    python3 ../train.py \
    --bs 256 \
    --lr 0.005 \
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后在命令行执行(假设当前处于 myproject 目录下)

    cd scripts && bash train.sh
    
    • 1

    即可开始训练。后续如果需要调参,修改 train.sh 里的数字即可。


    References

    [1] argparse — 命令行选项、参数和子命令解析器
    [2] argparse模块用法实例详解

  • 相关阅读:
    SpringBoot +Mybatis + Redis实现缓存(案例解析)
    手把手教你搭建Maven私服
    【SQL实用技巧】-- 分组内求topN问题
    一看就会,使用 nginx 作为 HTTP 负载均衡器
    Juniper交换机收集日志
    计算机毕业设计ssm基于SSM框架在线电影评论投票系统3gr0f系统+程序+源码+lw+远程部署
    cefsharp119.1.20(cef119.1.2,Chromium119.0.6045.105)版本升级体验及其他H264版本
    一、Verilog基础语法学习
    Spring Security实现基于RBAC的权限表达式动态访问控制
    Spring 中 Bean 对象的存储和取出
  • 原文地址:https://blog.csdn.net/raelum/article/details/126245099