彭某的技术折腾笔记

彭某的技术折腾笔记

Python Parser 教程

33
2023-10-05

Python Parser 教程

2023年10月5日

摘要

在编写 Python 程序时,经常会需要在运行时提供一些参数,以此来定义和决定一些运行时的行为。例如,在训练深度学习模型时,需要给定此次训练的轮数,学习率,预训练模型等。Python 中自带的 parser 以及 argparse 库可以很好的完成运行参数的解析。由于 argparse 远比 parser 强大,因此本文将完全基于 argparse 进行编写。

还有另外两个模块可以完成同样的任务,即 getopt (等价于 C 语言中的 getopt()) 和已被弃用的 optparse。 还要注意 argparse 是基于 optparse 的,因此用法与其非常相似。

Parser 信息来源

在 Python 中,有两个自带的容器,即 sys.orig_argvsys.argv,会自动储存本次运行时的命令行指令上下文。例如,当我们在命令行中输入以下命令时:

python main.py --value 2

sys.orig_argvsys.argv 会自动获得此命令中所有的 token:

# sys.orig_argv
['python', 'main.py', '--help']
# sys.argv
['main.py', '--help']

可以看到,sys.argv 只包含 Python 程序的后缀参数,而 sys.orig_argv 会包含完整的原始命令的 token,如果要获取有关解释器路径的信息则可以使用此版本。

Python 中的 Parser 会从 sys.argv 中获得命令行上下文参数,并将其按照用户自定义规则转换成运行时可用的 Python 参数,以供后续使用。

创建 Parser

Parser 包不需要额外安装,Python 自带。要创建一个 Parser,需要先导入 argparse 包:

import argparse

如果没有额外的要求,可以实例化一个最简单的 Parser 对象:

parser = argparse.ArgumentParser()

此后,再添加一句命令使 Parser 生效,即可将解析结果保存至 args

args = parser.parse_args()

Parser 自带一个 -h 和一个 --help 选项,不需要手动添加即存在,可以使用以下两条命令中的任意一个来查看当前 Parser 的帮助信息:

python main.py -h
python main.py --help

可以得到:

usage: main.py [-h]

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

一旦使用查看帮助信息的选项,程序将会在 parser.parse_args() 语句处终止,此行为在逻辑上是可以理解的。

然而,还可以在实例化时提供更多的参数来提供一些额外信息:

parser = argparse.ArgumentParser(description='This is description', 
                                 epilog='This is epilog')

此时帮助信息将变为:

usage: main.py [-h]

This is description

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

This is epilog

在 Parser 的默认行为中,支持在没有命名冲突的情况下只给出某个参数的前几个字符,例如如果有一个选项是 --batch_size,在运行程序时,输入过长的参数名效率很低,且容易输错,此时可以使用 --b, --ba, --bat 之类的简写,只需要没有其他前缀相同的参数名造成歧义即可。

如果想要关闭简写识别功能,实例化 Parser 对象时将 allow_abbrev 设置为 false 即可,例如:

parser = argparse.ArgumentParser(description='This is description',
                                 epilog='This is epilog',
                                 allow_abbrev=False)

本文将只涉及一些常用选项,完整选项可以查阅官方文档

创建自定义参数解析规则

除了自带的显示帮助信息的选项以外,开发者还可以添加各种自定义的参数解析规则。规则需添加在 Parser 实例化之后,解析之前。

两种类型的参数

位置参数

位置参数会按照出现的位置来进行匹配解析,不用在运行时提供参数名称,但由于位置参数是根据参数位置进行一对一的匹配,因此凡是设置了的规则都必须提供相应值,不能不提供。可以按照如下方式添加:

parser.add_argument('value_1')
parser.add_argument('value_2')

此时如果使用以下命令运行程序:

python main.py 1 2

args.value_1 将被自动被赋值为 1args.value_2 将被自动被赋值为 2

如果使用以下命令运行程序:

python main.py 1

则由于 args.value_2 未提供,将直接报错。

可选参数

可选参数,顾名思义,在需要时才显式的提供,可以按照如下方式添加:

# Only abbrevation
parser.add_argument('-v')
# Only full-name
parser.add_argument('--key')
# Both abbrevation and full-name
parser.add_argument('-n', '--name')

此时如果使用以下两个命令中的任意一个运行程序:

python main.py -v 1 --key abcd -n psc
python main.py -v 1 --key abcd --name psc

args.v 将被自动被赋值为 1args.key 将被自动被赋值为 abcdargs.name 将被自动被赋值为 psc

可选参数的最大的特点是,可以任意交换顺序,且默认情况下不是必须提供,如不提供则值为 None。以下几种用法完全一致:

python main.py -v 1 --key abcd --name psc
python main.py -v 1 --name psc --key abcd
python main.py --name psc -v 1 --key abcd

位置参数与可选参数混用

混用的逻辑非常简单,可选参数优先把其能够匹配的内容吸收,剩下的参数依次匹配给位置参数。例如:

parser.add_argument('value')
parser.add_argument('-n', '--name')

则无论使用以下哪一个命令:

python main.py 1 --name psc
python main.py --name psc 1

都是一样的结果,psc 将被 --name 吸收,剩下一个 1value 吸收。

高级设置

Parser 还提供一些高级选项为一些更具体的需求提供可能。

帮助信息

如果直接使用:

parser.add_argument('--value')

此时帮助信息将为:

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

有两个参数可以在帮助中提供一些关于此选项的信息,如果想为此参数提供更多信息,可以:

parser.add_argument('--value', 
                    metavar='META_NAME', 
                    help='This is help message')

则帮助信息会更加丰富,变为:

options:
  -h, --help         show this help message and exit
  --value META_NAME  This is help message

提供清晰的帮助信息是一个非常好的开发习惯,建议提供。

默认值

此设置仅对可选参数有效。

在默认情况下, 如果不给定某些参数,其默认值将会被自动设置为 None,例如:

parser.add_argument('value')
parser.add_argument('-n', '--name')

如果直接运行:

python main.py 123

args.name 由于没有被指定,将被自动被赋值为 None

如果想要指定此时的默认值,可以在选项中添加 default 参数:

parser.add_argument('-n', '--name', default='psc')

此时不给定此选项时的默认值将变成 psc,正常使用此选项时将覆盖默认值。

变量类型

默认情况下,所有参数的值都将以字符串的形式储存,即使使用例如:

python main.py --value 123

args.value 的值依然是字符串形式的 123

要设置变量类型,可以通过:

parser.add_argument('--value', type=int)

或是其他类型来进行设置。

限定值范围

有时我们希望运行时提供的参数值是某个集合中的一个元素,而不是想给什么就给什么,则可以使用 choices 来进行指定:

parser.add_argument('--value', type=int, choices=[1, 2, 3])

此时如果给定的值不在列表中,将会报错。

是否强制提供

此设置仅对可选参数有效。

可选参数在默认情况下支持部份提供,没提供的参数将储存默认值,因此,其 required 参数默认为 False。如果要强制用户运行时提供某些必须参数,可将其设置为 True

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

如果运行时忽略了 --value 则会报错。

动作设置

Parser 有一个选项用来设置当前参数的存储方式,名为 action,其默认值为 store,即直接存储此参数后面给定的值,若未提供则默认为 None,除此之外,还有一些其他的方式。

开关式选项

此设置仅对可选参数有效。

在有些情况下,我们需要的并不是在运行时通过命令行给定一些参数,而是通过启用某些选项来激活一些特殊值。此种选项不需要且不能读取命令行参数。

开关式选项有几种,最基本的形式可以在激活此选项时将参数设置为一个提前准备好的值,例如:

parser.add_argument('--value', action='store_const', const=100)

此时,如果不输入该参数,args.value 的值为默认值(默认值也可按照前文的方式修改)。若要激活此参数,可以:

python main.py --value

args.value 的值为提前设置好的 100

注意,此时不可像之前一样 --value 50 这样设置别的值,因为此时 --value 选项根本不会读取后文的参数。

除了自由度较高的 store_const 以外,还有两个更为常用的特例,store_truestore_false

parser.add_argument('--require_output', action='store_false')
parser.add_argument('--require_log', action='store_true')

此时,args.require_output 默认为 True,激活此选项时为 False,而 args.require_log 默认为 False,激活此选项时为 True

储存列表

有时会需要给同一个参数多个值,存在一个列表里,例如:

parser.add_argument('--values', action='append')

此后,可以多次使用 --value

python main.py --values 1 --values 2 --values 3

args.values 的值将为 [1, 2, 3]

指令计数

Parser 还提供了另一种方式用来获取参数激活次数:

parser.add_argument('--enable', '-e', action='count')

此时,无论是使用以下几种命令的任何一种:

python main.py --enable --enable
python main.py -e --enable
python main.py -e -e
python main.py -ee

args.enable 的值都为 2,因为其被激活了两次。

参数数量

固定参数数量

有时一个参数需要吸收紧跟其后的多个值,可以通过 nargs 来设置:

parser.add_argument('--values', nargs=2)

此时,如果使用:

python main.py --values 5 6

args.values 将储存一个列表 [5, 6]

需要注意的是,即使如果 nargs=1,其也将产生一个单元素列表而不是一个单一的值,这一点和不使用 nargs 有本质不同。

可变参数数量

除了固定个数以外,还可以吸收尽可能多的值:

parser.add_argument('--keys', nargs='+')
parser.add_argument('--values', nargs='*')

这两种用法都将吸收参数名后的所有值并存入列表,直到遇到下一个参数名为止。二者唯一的区别是 '+' 需要至少一个,否则报错,而 '*' 则没有这个要求。

单一且可选

最复杂的一种情况是,如果不使用某个参数,则储存默认值,如果使用但不给定对应值,则像开关式一样储存某个预设值,如果使用且给定一个值,则储存给定值:

parser.add_argument('--name', nargs='?', defaut='Nobody', const='Just a Person')

此时,以下三种运行方式将得到不同的结果:

# args.name = 'Nobody'
python main.py
# args.name = 'Just a Person'
python main.py --name
# args.name = 'PSC'
python main.py --name PSC

总结

Parser 是一个非常优秀的工具,可以让程序运行时的参数指定变得非常方便,结合外部配置文件使用将会极大程度的提高开发效率。

  • 0