【问题标题】:how to handle lists in automatic argument parsing with argparse如何使用 argparse 处理自动参数解析中的列表
【发布时间】:2020-11-25 09:03:51
【问题描述】:

I've written the following module that handles arguments automatically 用于我所有的 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]"

然后返回 为 list 参数,但我如何选择它以在解析中有条件地使用 ast.literal_eval ?

【问题讨论】:

  • type 参数应该是一个函数。它没有指定一个类。
  • 我试图在问题中加强我的语言,我指的是 type 函数的结果实际上,它给了我 ,我误解了重点吗?
  • type=type(val) 仅在 type 也是一个产生您想要的功能时才有效。 intfloat 工作。 boollist 不要。使用nargs时,对每个字符串应用类型函数。

标签: python python-3.x argparse


【解决方案1】:

将参数作为 Python 列表传递有点“烦人”,因为所有参数都是字符串,因此您需要将其解析为列表。如果您愿意妥协以空格分隔的参数传递列表,例如--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'

【讨论】:

    【解决方案2】:

    type 参数应该是一个函数 (callable),它接受一个字符串并返回一个值,或者引发错误。

    在内置函数中,intfloat 是唯一可用的函数:

    In [40]: int("12")
    Out[40]: 12
    In [41]: float("12.3")
    Out[41]: 12.3
    

    listbool 等其他人不会返回天真的用户期望的内容:

    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_evaljson.loads 处理。但这绕过了argparsenargstype=int 提供的保护措施。

    【讨论】:

      猜你喜欢
      • 2015-03-04
      • 2018-12-14
      • 2014-03-04
      • 2020-03-04
      • 2016-05-30
      • 2018-07-25
      • 1970-01-01
      • 1970-01-01
      • 2014-02-21
      相关资源
      最近更新 更多