使用argparse(python)创建变量键/值对

时间:2014-11-26 10:06:18

标签: python configuration argparse

我使用argparse模块设置命令行选项。我还在我的应用程序中使用dict作为配置。简单的键/值存储。

我正在寻找的是使用命令行参数覆盖JSON选项的可能性,而无需事先定义所有可能的参数。像--conf-key-1 value1 --conf-key-2 value2之类的东西,会创建一个字典{'key_1': 'value1','key_2': 'value2'}(' - '在参数中被替换为dict中的' _')。然后我可以将这个dict与我的JSON配置(dict)结合起来。

所以基本上我想将--conf-*定义为一个参数,其中*可以是任何键,后面的内容是value

我确实找到了configargparse模块,但据我所知,我从我已经使用的dict开始。

我有什么想法可以解决这个问题吗?

6 个答案:

答案 0 :(得分:5)

我要尝试的第一件事是使用parse_known_args来处理其他参数,并使用我的例行程序处理extras的列表。将“--conf-”处理添加到argparse会更有效。

argv = '--conf-key-1 value1 --conf-key-2 value2'.split()
p = argparse.ArgumentParser()
args, extras = p.parse_known_args(argv)

def foo(astr):
    if astr.startswith('--conf-'):
        astr = astr[7:]
    astr = astr.replace('-','_')
    return astr

d = {foo(k):v for k,v in zip(extras[::2],extras[1::2])}
# {'key_1': 'value1', 'key_2': 'value2'}

extras解析可能更强大 - 确保有正确的对,拒绝格式错误的密钥,处理=

另一种方法是扫描sys.argv --conf-字符串,并使用它们构建add_argument语句。

keys = [k for k in argv if k.startswith('--conf-')]
p = argparse.ArgumentParser()
for k in keys:
    p.add_argument(k, dest=foo(k))
print vars(p.parse_args(argv))

如果你接受'--conf key1 value1 --conf key2 value2 ...'作为输入,你可以定义

parser.add_argument('--conf', nargs=2, action='append')

会产生:

namespace('conf': [['key1','value1'],['key2','value2']])

可以很容易地变成字典。或者,自定义Action可以使用setattr(namespace, values[0], values[1])直接在命名空间中输入键/值对。

我认为有关于接受'“key1:value”“key2:value2”'输入的问题。

答案 1 :(得分:1)

我遇到了类似的问题,发现了一个非常可行的模式,可以与argparse一起很好地工作(这里有三个键对:foo,bar和baz:

mycommand par1 --set foo=hello bar="hello world" baz=5

1。定义可选的多值参数

必须将set参数定义为:

import argparse
parser = argparse.ArgumentParser(description="...")
...
parser.add_argument("--set",
                        metavar="KEY=VALUE",
                        nargs='+',
                        help="Set a number of key-value pairs "
                             "(do not put spaces before or after the = sign). "
                             "If a value contains spaces, you should define "
                             "it with double quotes: "
                             'foo="this is a sentence". Note that '
                             "values are always treated as strings.")
args = parser.parse_args()

该参数是可选的并且是多值的,最少出现一次(nargs='+')。

结果是字符串的列表,例如["foo=hello", "bar=hello world", "baz=5"]中的args.set,我们现在需要解析(注意shell如何处理和删除引号!)。

2。解析结果

为此,我们需要2个辅助函数:

def parse_var(s):
    """
    Parse a key, value pair, separated by '='
    That's the reverse of ShellArgs.

    On the command line (argparse) a declaration will typically look like:
        foo=hello
    or
        foo="hello world"
    """
    items = s.split('=')
    key = items[0].strip() # we remove blanks around keys, as is logical
    if len(items) > 1:
        # rejoin the rest:
        value = '='.join(items[1:])
    return (key, value)


def parse_vars(items):
    """
    Parse a series of key-value pairs and return a dictionary
    """
    d = {}

    if items:
        for item in items:
            key, value = parse_var(item)
            d[key] = value
    return d

这很简单:

# parse the key-value pairs
values = parse_vars(args.set)

您现在有了字典:

values = {'foo':'hello', 'bar':'hello world', 'baz':'5'}

请注意如何始终将值作为字符串返回。

此方法也记录为git gist

答案 2 :(得分:1)

使用str.split(delim, limit)可以轻松完成所有操作:

class kvdictAppendAction(argparse.Action):
    """
    argparse action to split an argument into KEY=VALUE form
    on the first = and append to a dictionary.
    """
    def __call__(self, parser, args, values, option_string=None):
        assert(len(values) == 1)
        try:
            (k, v) = values[0].split("=", 2)
        except ValueError as ex:
            raise argparse.ArgumentError(self, f"could not parse argument \"{values[0]}\" as k=v format")
        d = getattr(args, self.dest) or {}
        d[k] = v
        setattr(args, self.dest, d)

...


myparser.add_argument("--keyvalue",
                      nargs=1,
                      action=kvdictAppendAction,
                      metavar="KEY=VALUE",
                      help="Add key/value params. May appear multiple times.")

答案 3 :(得分:1)

top answer 的当前代码 有一个错误,如果“parse_var”函数中的“items”变量没有“=”,它会在赋值之前返回变量“value”。这是一个经过修改/精简的函数,它为每个参数中是否提供了“=”提供了一些错误处理:

def parse_vars(items):
    """
        Parse a series of key-value pairs and return a dictionary and 
        a success boolean for whether each item was successfully parsed.
    """
    count = 0
    d = {}
    for item in items:
        if "=" in item:
            split_string = item.split("=")
            d[split_string[0].strip()] = split_string[1].strip()
            count += 1
        else:
            print(f"Error: Invalid argument provided - {item}")
        
    return d, count == len(items)

答案 4 :(得分:0)

要稍微简化一下frausus答案,可以将两种方法轻松组合为一种。

注意:我的文档字符串等与我将其用于ansible extra_vars有所不同,但是字符串拆分的核心逻辑来自于fralaus的答案。

 def parse_vars(extra_vars):
     """
     Take a list of comma seperated key value pair strings, seperated
     by comma strings like 'foo=bar' and return as dict.
     :param extra_vars: list[str] ['foo=bar, 'key2=value2']

     :return: dict[str, str] {'foo': 'bar', 'key2': 'value2'}
     """
     vars_list = []
     if extra_vars:
         for i in extra_vars:
            items = i.split('=')
            key = items[0].strip()
            if len(items) > 1:
                value = '='.join(items[1:])
                vars_list.append((key, value))
     return dict(vars_list)

print parse_vars(args.set)
 $ test.py --set blah=gar one=too
>> {"blah": "gar", "one": "too"}

答案 5 :(得分:0)

您可以使用 vars() 将 'args' 对象的属性转换为 dict。

然后打印字典的键和值。

parser = argparse.ArgumentParser(description="Parameters")
parser.add_argument("--x", type=str, default="")
args, _ = parser.parse_known_args()

for k, v in sorted(vars(args).items()):
    print(k, '=', v)