如何使用 argparse 处理自动参数解析中的列表
how to handle lists in automatic argument parsing with argparse
对于我所有的 python 代码,我定义了我的程序参数的字典,然后 argparse 在解析调用序列时使用它来设置命令行参数和参数类型:
import argparse
def get_args(pars):
parser = argparse.ArgumentParser(description='Dynamic arguments')
# add each key of the default dictionary as an argument expecting the same type
for key,val in pars.items():
parser.add_argument('--'+key,type=type(val))
newpars=vars(parser.parse_args())
# Missing arguments=None need to be overwritten with default value
for key,val in newpars.items():
if val==None:
newpars[key]=pars[key]
return(newpars)
if __name__ == "__main__":
# default values:
pars={"a":1,"animal":"cat","f":3.3}
pars=get_args(pars)
print(pars)
这适用于解析浮点数、整数和字符串...
python3 test.py --f 2.1
{'a': 1, 'animal': 'cat', 'f': 2.1}
但现在我想添加一个可以是数字列表的参数,例如假设我的默认 pars 字典是这样的:
pars={"a":1,"animal":"cat","f":3.3,"l":[1]}
现在发生这种情况:
python3 test.py --l [1,2,3]
{'a': 1, 'animal': 'cat', 'f': 3.3, 'l': ['[', '1', ',', '2', ',', '3', ']']}
这不是我想要的,我想要一个数字列表。过去,当我手动一个接一个地处理参数时,我会通过使用
来解决这个问题
import ast
val=ast.literal_eval("[1,2,3]")
[1, 2, 3]
但是我不能在每个参数上使用 ast.literal_eval,因为当没有传递包含数字参数的字符串时它会失败,所以我想试试这个:
try:
parser.add_argument('--'+key,type=type(ast.literal_eval(val)))
except:
parser.add_argument('--'+key,type=type(val))
并将默认列表定义为
l="[1]"
this then returns 作为列表参数,但我如何选择它以在解析中有条件地使用 ast.literal_eval ?
将参数作为 Python 列表传递有点“烦人”,因为所有参数都是字符串,因此您需要将其解析为列表。如果您愿意妥协将列表作为 space 分隔的参数传递,例如 --l 1 2 3
,那么一种可能的方法是使用 nargs
。只需在循环中添加一个 try/except
块来检查参数是否可迭代:
for key, val in pars.items():
try:
typ = type(val[0])
nargs = "+"
except TypeError:
typ = type(val)
nargs = None
parser.add_argument('--'+key, nargs=nargs, type=typ)
一些注意事项:
这允许传递任意长度的列表。如果你想将大小限制为字典中的列表,只需更改为 nargs = len(val)
.
这只能允许同类列表 - 里面的所有对象必须是同一类型。
我使用了 nargs = None
而不是 nargs = 1
因为后者实际上会使参数成为一个 1 项列表,而前者会将参数作为单个对象传递。
将以上代码嵌入您的代码(并使用下面的第二个 par
,以提高可读性)将导致:
pars = {"a": 1, "animal": "cat", "f": 3.3, "l": [1]}
>>> python3 test.py --f 2.1 --l 1 2
{'a': 1, 'animal': 'cat', 'f': 2.1, 'l': [1, 2]}
>>> python3 test.py --f 2.1 --l 1 2.2
test.py: error: argument --l: invalid int value: '2.2'
type
参数应该是一个接受字符串的函数 (callable
),return 是一个值,否则会引发错误。
在内置函数中,int
和 float
大约是唯一可用的函数:
In [40]: int("12")
Out[40]: 12
In [41]: float("12.3")
Out[41]: 12.3
其他像 list
或 bool
不 return 天真的用户所期望的:
In [42]: list("[1,2,3]")
Out[42]: ['[', '1', ',', '2', ',', '3', ']']
In [43]: bool("False")
Out[43]: True
因此,如果你想接受'--flag False'(而不是使用'store_true' action
),你需要一个自定义函数
def mybool(astr):
if astr in ['False', 'no', 'No']:
return False
elif ...
接受列表的预期方式是使用 nargs
指定数量。
$python myscript.py --foo [1,2,3] --bar a b c --baz [1 2]
翻译成sys.argv
为
['myscript.py', '--foo', '[1,2,3]', '--bar', 'a', 'b', 'c', '--baz', '[1', '2]']
nargs='+'
将处理 '--bar' 情况,但不会处理其他情况。
'--foo' 可以用 type=ast.literal_eval
或 json.loads
处理。但这绕过了 argparse
为 nargs
和 type=int
.
提供的保护措施
import argparse
def get_args(pars):
parser = argparse.ArgumentParser(description='Dynamic arguments')
# add each key of the default dictionary as an argument expecting the same type
for key,val in pars.items():
parser.add_argument('--'+key,type=type(val))
newpars=vars(parser.parse_args())
# Missing arguments=None need to be overwritten with default value
for key,val in newpars.items():
if val==None:
newpars[key]=pars[key]
return(newpars)
if __name__ == "__main__":
# default values:
pars={"a":1,"animal":"cat","f":3.3}
pars=get_args(pars)
print(pars)
这适用于解析浮点数、整数和字符串...
python3 test.py --f 2.1
{'a': 1, 'animal': 'cat', 'f': 2.1}
但现在我想添加一个可以是数字列表的参数,例如假设我的默认 pars 字典是这样的:
pars={"a":1,"animal":"cat","f":3.3,"l":[1]}
现在发生这种情况:
python3 test.py --l [1,2,3]
{'a': 1, 'animal': 'cat', 'f': 3.3, 'l': ['[', '1', ',', '2', ',', '3', ']']}
这不是我想要的,我想要一个数字列表。过去,当我手动一个接一个地处理参数时,我会通过使用
来解决这个问题import ast
val=ast.literal_eval("[1,2,3]")
[1, 2, 3]
但是我不能在每个参数上使用 ast.literal_eval,因为当没有传递包含数字参数的字符串时它会失败,所以我想试试这个:
try:
parser.add_argument('--'+key,type=type(ast.literal_eval(val)))
except:
parser.add_argument('--'+key,type=type(val))
并将默认列表定义为
l="[1]"
this then returns
将参数作为 Python 列表传递有点“烦人”,因为所有参数都是字符串,因此您需要将其解析为列表。如果您愿意妥协将列表作为 space 分隔的参数传递,例如 --l 1 2 3
,那么一种可能的方法是使用 nargs
。只需在循环中添加一个 try/except
块来检查参数是否可迭代:
for key, val in pars.items():
try:
typ = type(val[0])
nargs = "+"
except TypeError:
typ = type(val)
nargs = None
parser.add_argument('--'+key, nargs=nargs, type=typ)
一些注意事项:
这允许传递任意长度的列表。如果你想将大小限制为字典中的列表,只需更改为
nargs = len(val)
.这只能允许同类列表 - 里面的所有对象必须是同一类型。
我使用了
nargs = None
而不是nargs = 1
因为后者实际上会使参数成为一个 1 项列表,而前者会将参数作为单个对象传递。
将以上代码嵌入您的代码(并使用下面的第二个 par
,以提高可读性)将导致:
pars = {"a": 1, "animal": "cat", "f": 3.3, "l": [1]}
>>> python3 test.py --f 2.1 --l 1 2
{'a': 1, 'animal': 'cat', 'f': 2.1, 'l': [1, 2]}
>>> python3 test.py --f 2.1 --l 1 2.2
test.py: error: argument --l: invalid int value: '2.2'
type
参数应该是一个接受字符串的函数 (callable
),return 是一个值,否则会引发错误。
在内置函数中,int
和 float
大约是唯一可用的函数:
In [40]: int("12")
Out[40]: 12
In [41]: float("12.3")
Out[41]: 12.3
其他像 list
或 bool
不 return 天真的用户所期望的:
In [42]: list("[1,2,3]")
Out[42]: ['[', '1', ',', '2', ',', '3', ']']
In [43]: bool("False")
Out[43]: True
因此,如果你想接受'--flag False'(而不是使用'store_true' action
),你需要一个自定义函数
def mybool(astr):
if astr in ['False', 'no', 'No']:
return False
elif ...
接受列表的预期方式是使用 nargs
指定数量。
$python myscript.py --foo [1,2,3] --bar a b c --baz [1 2]
翻译成sys.argv
为
['myscript.py', '--foo', '[1,2,3]', '--bar', 'a', 'b', 'c', '--baz', '[1', '2]']
nargs='+'
将处理 '--bar' 情况,但不会处理其他情况。
'--foo' 可以用 type=ast.literal_eval
或 json.loads
处理。但这绕过了 argparse
为 nargs
和 type=int
.