我使用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
开始。
我有什么想法可以解决这个问题吗?
答案 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
必须将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个辅助函数:
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)