导言:对于一些功能相仿,但是其输入参数并不相同的程序,我们往往在进入main函数时使用同一个parser进行参数的解析。当然可以针对不同的.py文件写出不同的参数读取的入口,但是往往那样并不优雅。使用subparser可以重用接口。
1. parse 以及 subparsers:
parse大家都很熟悉了,用来读取输入main函数的数据。所以本文的重点不是parse,主要落于subparsers
每一个subparser可以继承父类的parse,并使用其参数接口。
1.1 例子
1 | # add sub-parser |
k
是键,{"recognition" "demo"}
,即parser的名称,注意后面使用parse_args()时所输入的子parser器名称必须要和其一样。不然会报错。详细见下面例子
实例:1
2
3python3 main.py recognition -h #正确调用,因为parser有名为recognition的parse
python3 main.py sdeqds -h #错误,找不到名为sdeqds子parse。
在terminal使用python进行程序的运行的时候先要指定子解释器的名字,在输入参数。
p
是值,包含了两个类。
p.get_parser()
可以进入到该类的get_parser
方法中
在对应的类中,get_parser
都会执行,每一个get_parser
都会返回一个parser类,赋值给add_parser中的parents参数。
subparsers = parser.add_subparsers(dest='processor')
得到的subparsers
可以有多个parser
,相当于parser
的子parsers
的句柄。向其中加入parser
使用add_parser
即可
parser.add_subparsers(dest=’processor’)返回的是子parser的句柄!!
使用add_parser()向subparsers中加入parser
add_parser(“名字”,parent = “parser”)。
子类中的parser的构造
recognition
类的get_parser
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22def get_parser(add_help=False):
# parameter priority: command line > config > default
parent_parser = Processor.get_parser(add_help=False)
parser = argparse.ArgumentParser(
add_help=add_help,
parents=[parent_parser],
description='Spatial Temporal Graph Convolution Network')
# parents=[parent_parser]有点类似与克隆。
# region arguments yapf: disable
# evaluation
parser.add_argument('--show_topk', type=int, default=[1, 5], nargs='+', help='which Top K accuracy will be shown')
# optim
parser.add_argument('--base_lr', type=float, default=0.01, help='initial learning rate')
parser.add_argument('--step', type=int, default=[], nargs='+', help='the epoch where optimizer reduce the learning rate')
parser.add_argument('--optimizer', default='SGD', help='type of optimizer')
parser.add_argument('--nesterov', type=str2bool, default=True, help='use nesterov or not')
parser.add_argument('--weight_decay', type=float, default=0.0001, help='weight decay for optimizer')
# endregion yapf: enable
return parser
调用处:subparsers.add_parser(k, parents=[p.get_parser()])
返回了一个parser
这个parser
继承了Processor的parser,并且添加了自己的参数。
各个类的父子关系:1
2
3|IO----|----demo
|
|----processor----|----REC_Processor
subparser
的作用可以复用相同的参数接口
所有parser写完后
调用根parse进行解析:
parser = argparse.ArgumentParser(description='Processor collection')
subparsers = parser.add_subparsers(dest='processor')
,添加子解析器
…arg = parser.parse_args()
,开启解析,定义了所有参数之后,你就可以给parse_args()
传递一组参数字符串来解析命令行。默认情况下,参数是从 sys.argv[1:] 中获取,但你也可以传递自己的参数列表。选项是使用GNU/POSIX
语法来处理的,所以在序列中选项和参数值可以混合。
parse_args() 的返回值是一个命名空间,包含传递给命令的参数。该对象将参数保存其属性,因此如果你的参数 dest
是 myoption
,那么你就可以args.myoption
来访问该值。
- 可以自己向parse_args中传递参数:
1
parser.parse_args(['-a', '-bval', '-c', '3'])
如果不parse_args()
不加参数则是默认从sys.argv[1:]
来传入
argparse.add_argument() dest参数的意义:
subparsers = parser.add_subparsers(dest='processor')
dest
指定的值用作key
值,从解析后的对象中取出用户输入的第一个参数
所以上述的parser拥有一个arg.processor的属性。而这个属性对应了cmd中第一个输入值,也等于sys.argv[1]
add_argument()函数中参数的定义以及作用:
参数名称:
add_argument()必须知道参数是可选的还是必须的位置参数,第一个传递给add_arguments的参数必须是可选参数或者是位置参数,例如,下面是可选参数。1
2
3
4...
parser.add_argument('--use_gpu', type=str2bool, default=True, help='use GPUs or not')
parser.add_argument('bar') #位置参数,必须给出
...
- 其中双横线
--
代表不能省略,单横线-
代表简写名字,别名
type参数:
type
表明该参数的类型,可以使用函数进行自定义类型的搭建:1
2
3
4
5
6
7
8
def str2bool(v):
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
其中的返回值如果有限制必须要返回到限制域中,不在限制里的其他输入需要进行异常处理。
action参数:
当输入参数需要输入列表、字典类型时可以使用action进行传递:
store_const:
store_const:存储const指定的值。1
2
3
4
5
6
parser=argparse.ArgumentParser()
parser.add_argument('--foo',action='store_const',const=42)
parser.parse_args('--foo'.split())
# Namespace(foo=42)
append
1 |
|
append:保存为列表格式,将每个参数的值添加到这个列表。
自定义Action类:
在argparse中使用Action自定义类是为了可以让输入参数的类型自定义化,可以让其输入列表、字典等参数。
1 | # /processor.py get_parser() |
可以通过继承Action类来实现自定义action类型,通过继承argparse.action,并提供call()方法,提供四个参数。
- parser:ArgumentParser对象。
- namespace:parse_args()返回的命名空间。
- values:相关联的命令行参数
- option_string:可选字符串,用来触发action,如果没有指定,就通过位置参数来关联。
在__call__(self, parser, namespace, values, option_string=None)
中使用setattr(namespace, self.dest, output_dict)
来进行赋值。self.dest
指代的是调用call的那个parser。