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

问题描述 投票:6回答:3

我正在使用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开始。

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

python configuration argparse
3个回答
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
投票

我遇到了类似的问题,发现了一个非常适合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


0
投票

为了简化fralaus答案,这两种方法可以轻松组合成一种。

注意:我的docstring等因为我将它用于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"}
© www.soinside.com 2019 - 2024. All rights reserved.