熟悉 Linux 命令行的用户享受着对于不同命令指定不同参数的遍历,如 ls,表示打印当前目录下内容,而使用 ls /home 能够打印目录 /home 下的内容。这增加了 Linux 命令的使用范围,提高了用户体验。同样,在 Python 中也有类似的功能模块,那就是内置模块argparse, 其不仅能够自定义解析命令行参数(从 sys.argv 中解析这些参数),还能够解析 JSON 文件。本篇所有命令都是在 jupyter 中演示。

基本步骤

使用 argparse 常用步骤:

  1. import argparse # 导入模块;
  2. parser = argparse.ArgumentParser(prog="工程名", description="自定义描述信息") # 实例化一个解析器,在 jupyter 中使用 argparse.ArgumentParser? 可查看更详细的参数内容;
  3. parser.add_argument("-x", "--var_x", type=int, help="传入自变量") # 添加想要解析的命令行(可选)参数 var_x(例子)及配置,下面会对个配置进行详解。该步骤可重复添加多个不同的参数;
  4. args = parser.parse_args() # 解析参数;
  5. args.__dict__.update(d) # 使用字典 d 更新参数;
  6. args.var_x # 打印或使用某个参数;

命令行调用例子:

1
2
# 用户自定义参数 var_x 的值,运行 main.py,得到结果
python main.py --var_x 3

上面的步骤中,用户可灵活使用步骤 3 自定义解析参数,下面进行详细解释。

add_argument 配置

在上面步骤 3 中,用户可自定义添加待解析的参数以及该参数的配置,如取值类型、取值范围、默认值、参数帮助信息等。常用的配置如下:

配置项名 描述 取值
name or flags 一个命令或者一个选项字符串的列表,例如位置参数 var_x 或者可选参数 -f, --foo 字符串
action 指定如何处理该参数 'store', 'store_const', 'store_true', 'append', 'append_const', 'count', 'help', 'version'
choices 将参数取值限制在特定范围内 [1, 2]range(10) 或其他容器对象
const 存储一个常量值
default 当参数在命令行为指定值时使用的默认值 默认为 None
dest 指定结果命名空间(namespace)中使用的属性名称
help 参数的帮助信息
metavar 参数的备用显示名称,如帮助中所示
nargs 参数可以使用的次数 int'?''*''+'argparse.REMAINDER
required 指示参数是必需的还是可选的 TrueFalse
type 自动将参数转换为给定类型 intfloatargparse.FileType('w') 或 可调用函数

name or flags

add_argument() 方法必须知道是否需要可选参数(如 -x--var_x)或位置参数(如 var_x)。因此,传递给 add_argument() 的第一个参数必须是一系列标志或一个简单的参数名称。

可选参数创建方法:

1
parser.add_argument('-x', '--var_x')

位置参数创建方法:

1
parser.add_argument('var_x')

可选参数在命令行解析时,必须指定可选参数的名字,位置参数则是按照添加 add_argument 的位置依次赋值,当遇到可选参数时自动跳过。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
%%writefile /workspace/hello/t.py

import argparse

def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("x")
parser.add_argument("-z", "--var_z")
parser.add_argument("y")
return parser

if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print("x: ", args.x)
print("z: ", args.var_z)
print("y: ", args.y)
1
2
3
4
%%bash
cd /workspace/hello/
python t.py 1 -z 2 3
# python t.py 1 --var_z 2 3

结果

1
2
3
x:  1
z: 2
y: 3

action

ArgumentParser 对象将命令行参数与动作相关联。这些动作可以做与它们相关联的命令行参数的任何事,尽管大多数动作只是简单的向 parse_args() 返回的对象上添加属性。action 命名参数指定了这个命令行参数应当如何处理。常用的动作有: 'store', 'store_const', 'store_true', 'append', 'append_const', 'count', 'help', 'version', 'extend'

这里只针对 store_true 进行演示,其他参见:argparse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-v", "--verbose", action="store_true")
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
if args.verbose:
print("long logs")
else:
print("short logs")

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py --verbose

结果

1
long logs

如果不指定,则赋值 false

1
2
3
%%bash
cd /workspace/hello/
python t.py

结果

1
short logs

choices

命令行赋值时,只有取值在 choices 中的才通过。例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-x", "--var_x", choices=["0a", "0b", "0c"])
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print(args.var_x)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py -x 0a

结果

1
0a

如果取值不在 choices 中则报错。

const

不常用

default

有时候,一些参数在命令行赋值时常被忽略,如果这些参数有 default 默认值,则不会导致程序运行中出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument('-x', "--var_x", default=42)
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print(args.var_x)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py

结果

1
42

dest

定义参数在代码中的新名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-x", "--var_x", dest="varX")
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print(args.varX) # 调用时使用区别于命令行显示的变量名

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py -x 3

结果

1
3

help

显示参数的帮助信息。当用户请求帮助时(在命令行使用 -h--help 的方式)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-x", "--var_x", help="自变量 x 的值")
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print(args.var_x)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py -h

结果

1
2
3
4
5
6
7
usage: t.py [-h] [-x VAR_X]

python argparser

options:
-h, --help show this help message and exit
-x VAR_X, --var_x VAR_X 自变量 x 的值

metavar

help 一节中可以发现打印的帮助信息,变量名都是大写,使用参数 metavar 可以指定打印时变量名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-x", "--var_x", metavar='var-x')
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print(args.var_x)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py -h

结果

1
2
3
4
5
6
7
usage: t.py [-h] [-x var-x]

python argparser

options:
-h, --help show this help message and exit
-x var-x, --var_x var-x

nargs

add_argument() 方法中如果不添加 nargs 参数,则默认在命令行中可选或位置参数的后面值作为参数的取值,如果增加 nargs,则命令可选或位置参数后的 nargs 个值作为一个列表元素赋值给参数。nargs 支持的值有:

  1. N # 一个整数
  2. ‘?’
  3. ‘+’ # 匹配参数后面至少一个值,可匹配多个值
  4. ‘*’ # 匹配参数后面至少0个值,可匹配多个值

更多请参考:argparse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-x", "--var_x")
parser.add_argument("-t", "--theta", nargs=2)
parser.add_argument("-g", "--gamma", nargs=1)
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print("var_x: ", args.var_x)
print("theta: ", args.theta)
print("gamma: ", args.gamma)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py -x 1 -t 30 60 -g 0.5

结果

1
2
3
var_x:  1
theta: ['30', '60']
gamma: ['0.5']

required

通常,argparse 模块会认为 -f--bar 等旗标是指明 可选的 参数,它们总是可以在命令行中被忽略。 要让一个选项成为 必需的,则可以将 True 作为 required= 关键字参数传给 add_argument():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("-x", "--var_x", required=True)
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print("var_x: ", args.var_x)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py -x 1

结果

1
var_x:  1

如果在命令行不指定参数则会报错,即使增加了 default 参数也会报错。

type

默认情况下,解析器会将命令行解析的值作为字符串赋值给参数,通过指定 type 可以将解析为指定类型的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%%writefile /workspace/hello/t.py

import argparse


def t():
parser = argparse.ArgumentParser(description="python argparser")
parser.add_argument("count", type=int)
parser.add_argument("distance", type=float)
parser.add_argument("street", type=ascii, help="ascii 码字符")
parser.add_argument("code_point", type=ord, help="单个字符")
return parser


if __name__ == "__main__":
parser = t()
args = parser.parse_args()
print("count: ", args.count)
print("distance: ", args.distance)
print("street: ", args.street)
print("code_point: ", args.code_point)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py 1 2.0 'a' '天'

结果

1
2
3
4
count:  1
distance: 2.0
street: 'a'
code_point: 22825

解析 JSON

有时候,一个工程有多个开发人员开发,每个子模块都有自定义的参数,常常将参数记录在 JSON 中,使用时直接从 JSON 中读取参数的取值即可。

读取 JSON

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
%%writefile /workspace/hello/t.py

import argparse
import os, json

def parse_json(json_file):

parser = argparse.ArgumentParser(description="python argparser")
if json_file is not None and os.path.exists(json_file):
with open(json_file, 'r') as jf:
parser.__dict__.update(json.load(jf))
return parser


if __name__ == "__main__":
# 把参数写入JSON
d = {'a': 1, 'b': 2}
json_file = 't.json'
with open(json_file, 'w') as jf:
json.dump(d, jf)
# 从JSON中读取参数
args = parse_json(json_file)
print(args)
print(args.a)
print(args.b)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py

结果

1
2
3
ArgumentParser(prog='t.py', usage=None, description='python argparser', formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
1
2

JSON 更新参数

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
%%writefile /workspace/hello/t.py

import argparse
import json
import os


def parse_json(json_file):
parser = argparse.ArgumentParser(description="python argparser")
if json_file is not None and os.path.exists(json_file):
with open(json_file, "r") as jf:
parser.__dict__.update(json.load(jf))
return parser


def update_parse(args, json_file):
if json_file is not None and os.path.exists(json_file):
with open(json_file, "r") as jf:
args.__dict__.update(json.load(jf))
return args


if __name__ == "__main__":
# 把参数1写入JSON
d1 = {"a": 1, "b": 2}
json_file = "t.json"
with open(json_file, "w") as jf:
json.dump(d1, jf)
# 把参数2写入JSON
d2 = {"a": 10, "b": 20}
json_file_new = "t-new.json"
with open(json_file_new, "w") as jf:
json.dump(d2, jf)
# 从JSON中读取参数
args = parse_json(json_file)
print(args)
print(args.a)
print(args.b)
args = update_parse(args, json_file_new)
print(args)
print(args.a)
print(args.b)

运行

1
2
3
%%bash
cd /workspace/hello/
python t.py

结果

1
2
3
4
5
6
ArgumentParser(prog='t.py', usage=None, description='python argparser', formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
1
2
ArgumentParser(prog='t.py', usage=None, description='python argparser', formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
10
20

注意

  1. 添加参数时可以用 -,但是使用该参数时需要替换为 _,如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import argparse

    parser = argparse.ArgumentParser(description='test')
    parser.add_argument('--seed-test', type=int, default=72, help='Random seed.')
    args = parser.parse_args()

    if __name__ == "__main__":
    print(args)
    print(args.seed_test)

参考文献

  1. argparse — 命令行选项、参数和子命令解析器
  2. Argparse 教程
  3. argparse模块用法实例详解