python命令行解析模块argparse用法小结

前言

argparse是python的命令行解析的标准模块,内置于python,不需要安装,用于命令行参数的解析,这意味着我们无需在代码中手动为变量赋值,而是可以直接在命令行中向程序传递相应的参数,再由变量去读取这些参数

使用时只需导入:

import argparse

在下面介绍它的具体用法之前,我们先通过一个简单的例子,感受一下它使用的大致步骤。

""" 求解两数之和 """

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(sum(first_num, second_num))

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

python3 demo.py -h

或者:

python3 demo.py --h

可以看到相应的帮助信息:  

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

在命令行中执行:

 创建一个ArgumentParser 对象

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

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

prog

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

 先来看一个例子:

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

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

import argparse

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

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

usage

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

这里依然使用前言的例子:

  

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

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

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

相应的帮助信息变成:

usage: See the options below for usage

options:
  -h, --help  show this help message and exit
  --a A       first number
  --b B       second number

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

description:

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

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

相应的帮助信息,

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

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

epilog

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

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

相应的帮助信息,

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.

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

add_argument(),向解析器中添加命令行参数

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

ArgumentParser.add_argument(
	name or flags...,
	nargs,
	default,
	type,
	choices,
	required,
	help
)

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

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()

相应的帮助信息:

usage: demo.py [-h] [-i INSTALL]

options:
  -h, --help            show this help message and exit
  -i INSTALL, --install INSTALL

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

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

parser = argparse.ArgumentParser()
parser.add_argument('param1')
parser.add_argument('param2')
args = parser.parse_args()

相应的帮助信息:

usage: demo.py [-h] param1 param2

positional arguments:
  integer

options:
  -h, --help  show this help message and exit

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

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

parser = argparse.ArgumentParser(prog="My Program")
parser.add_argument("-a")
parser.add_argument("--b")
parser.add_argument("c")
args = parser.parse_args()
x = args.a
y = args.b
z = args.c
print(x,"\n",y,"\n",z)

 

 

 

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

type

顾名思义,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))

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

<class 'str'> <class 'int'>

default

default 指选项的默认值,例如,  

parser = argparse.ArgumentParser()
parser.add_argument('--a', type=int, default=5)
args = parser.parse_args()
print(args.a)

  • 直接执行 python3 demo.py ,将会输出 5,因为采用了默认值。
  • 如果执行 python3 demo.py --a x 则会输出 x(x 是任何整数,且不能省略)。
  • 如果没有为 --a 指定默认值,且在命令行执行时也没有向 --a 传参,则 args.a 为 None

如果将a改为位置参数, 

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('a', type=int, default=5)
args = parser.parse_args()
print(args.a)

 required

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

例如,

parser = argparse.ArgumentParser()
parser.add_argument('--a')
args = parser.parse_args()

此时帮助信息为:

usage: demo.py [-h] [--a A]

options:
  -h, --help  show this help message and exit
  --a A

现在指定 required=True,

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

这时候帮助信息变为:

usage: demo.py [-h] --a A

options:
  -h, --help  show this help message and exit
  --a A

可以看到 [] 消失了,说明 --a 变成了必选项。虽然变成了像和位置参数一样都是必选的,但是我们可以按照关键字参数的形式传递参数

help

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

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

相应的帮助信息:

usage: demo.py [-h] [--lr LR]

options:
  -h, --help  show this help message and exit
  --lr LR     learning rate

nargs

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

parser = argparse.ArgumentParser()
parser.add_argument('--a', type=int, nargs=5)
args = parser.parse_args()
print(args.a)

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

[1, 2, 3, 4, 5]

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

更进一步,

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

choices

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

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--a', type=int, choices=[1, 3, 5])
parser.add_argument('b', type=int, choices=[11, 33, 55])

args = parser.parse_args()

 如果实际传递的不是这里面的值,就将报错。

 parse_args(),解析命令行参数

我们来看一个例子,

import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('--a', type=int)
parser.add_argument('--b', type=int)
print(sys.argv)

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

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

从中可以看出,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)

执行 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)

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

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

可以看出,如果在命令行中没有提供相应的选项,并且该选项也没有默认值,则在命名空间中该选项的值为 None, 

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()

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

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

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

try:
    args = parser.parse_args()
except:
    parser.print_help()
    sys.exit(0)

使用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

其中 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

# 其他代码...

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

# 用来确保无论在哪里执行该脚本,都能够正确返回该脚本所在的目录,以便后续根据这个目录来定位所要运行程序的相对位置
cd "$(dirname $0)"

python3 ../train.py \
--bs 256 \
--lr 0.005 \

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

cd scripts && bash train.sh

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

  

posted on 2022-09-24 22:16  朴素贝叶斯  阅读(1413)  评论(0)    收藏  举报

导航