我正在尝试将字典设置为可选参数(使用argparse);以下是我到目前为止的内容:
parser.add_argument('-i','--image', type=dict, help='Generate an image map from the input file (syntax: {\'name\': <name>, \'voids\': \'#08080808\', \'0\': \'#00ff00ff\', \'100%%\': \'#ff00ff00\'}).')
但是运行脚本:
$ ./script.py -i {'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}
script.py: error: argument -i/--image: invalid dict value: '{name:'
即使在翻译中,
>>> a={'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}
工作正常。
那么我应该如何传递参数呢? 提前谢谢。
答案 0 :(得分:46)
暗示这个:json.loads
也在这里工作。它似乎并不太脏。
import json
import argparse
test = '{"name": "img.png","voids": "#00ff00ff","0": "#ff00ff00","100%": "#f80654ff"}'
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=json.loads)
args = parser.parse_args(['-i', test])
print(args.input)
返回:
{u'0': u'#ff00ff00', u'100%': u'#f80654ff', u'voids': u'#00ff00ff', u'name': u'img.png'}
答案 1 :(得分:8)
为了完整性,与json.loads类似,您可以使用yaml.load(可从PyPI中的PyYAML获得)。这比json更有优势,因为不需要在命令行上引用单个键和值,除非您试图将整数强制转换为字符串或以其他方式克服yaml转换语义。但显然整个字符串需要引用,因为它包含空格!
>>> import argparse
>>> import yaml
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-fna', '--filename-arguments', type=yaml.load)
>>> data = "{location: warehouse A, site: Gloucester Business Village}"
>>> ans = parser.parse_args(['-fna', data])
>>> print ans.filename_arguments['site']
Gloucester Business Village
虽然在给出的问题中不可否认,但是必须引用或改写许多键和值以防止yaml被殴打。如果您需要数字而不是字符串值,使用以下数据似乎非常有效:
>>> parser.add_argument('-i', '--image', type=yaml.load)
>>> data = "{name: img.png, voids: 0x00ff00ff, '0%': 0xff00ff00, '100%': 0xf80654ff}"
>>> ans = parser.parse_args(['-i', data])
>>> print ans.image
{'100%': 4161164543L, 'voids': 16711935, 'name': 'img.png', '0%': 4278255360L}
答案 2 :(得分:4)
我打赌你的shell是fu&gt;&lt; 0大括号(见here)。我将使用Argument groups在CLI中逐个传递args,并以编程方式构建dict。
传入一个像字典一样复杂的python对象,迫使用户知道python语法,对我来说似乎有点难过。
答案 3 :(得分:3)
你绝对可以在参数解析器中获得看起来像字典文字的内容,但是你必须引用它,所以当shell解析你的命令行时,它会以
形式出现。所以像这样的东西可以把你想要的文字带到你的程序中:
python MYSCRIPT.py -i "{\"name\": \"img.png\", \"voids\": \"#00ff00ff\",\"0\": \"#ff00ff00\",\"100%\": \"#f80654ff\"}"
但是,此字符串不是dict构造函数的有效参数;相反,它是一个有效的python代码片段。您可以告诉您的参数解析器该参数的“类型”是eval
,这将起作用:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i','--image', type=eval, help='Generate an image map...')
args = parser.parse_args()
print args
并称之为:
% python MYSCRIPT.py -i "{\"name\": \"img.png\", \"voids\": \"#00ff00ff\",\"0\": \"#ff00ff00\",\"100%\": \"#f80654ff\"}"
Namespace(image={'0': '#ff00ff00', '100%': '#f80654ff', 'voids': '#00ff00ff', 'name': 'img.png'})
但这不安全;输入可以是任何东西,你正在评估任意代码。它同样笨拙,但以下会更安全:
import argparse
import ast
parser = argparse.ArgumentParser()
parser.add_argument('-i','--image', type=ast.literal_eval, help='Generate an image map...')
args = parser.parse_args()
print args
这也有效,但对于允许eval
'd的限制更为严格。
但是,让用户输入正确引用的东西,看起来像命令行上的python字典,这是非常笨拙的。并且,你必须在事后做一些检查,以确保他们传入字典而不是其他可评估的东西,并且在其中有正确的密钥。如果符合以下条件,则更容易使用:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--image-name", required=True)
parser.add_argument("--void-color", required=True)
parser.add_argument("--zero-color", required=True)
parser.add_argument("--full-color", required=True)
args = parser.parse_args()
image = {
"name": args.image_name,
"voids": args.void_color,
"0%": args.zero_color,
"100%": args.full_color
}
print image
有关:
% python MYSCRIPT.py --image-name img.png --void-color \#00ff00ff --zero-color \#ff00ff00 --full-color \#f80654ff
{'100%': '#f80654ff', 'voids': '#00ff00ff', 'name': 'img.png', '0%': '#ff00ff00'}
答案 4 :(得分:2)
使用简单的lambda解析非常灵活:
parser.add_argument(
'--fieldMap',
type=lambda v: {k:int(v) for k,v in (x.split(':') for x in v.split(','))},
help='comma-separated field:position pairs, e.g. Date:0,Amount:2,Payee:5,Memo:9'
)
答案 5 :(得分:2)
将@Edd的type=
部分和@Bradley的ast.literal.eval
部分组合起来,可以得出最直接的解决方案IMO。它可以直接检索argval,甚至可以为dict使用(引用的)默认值:
parser.add_argument('--params', '--p', help='dict of params ', type=ast.literal.eval, default="{'name': 'adam'}")
args = parser.parse_args()
python test.py --p "{'town': 'union'}"
请注意dict值上的引号。此引用在Windows和Linux(已通过[t] csh测试)上有效。
dict=args.params
答案 6 :(得分:1)
我发现的一种最简单的方法是将字典解析为列表,然后将其转换为字典。例如,使用Python3:
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--image', type=str, nargs='+')
args = parser.parse_args()
if args.image is not None:
i = iter(args.image)
args.image = dict(zip(i, i))
print(args)
然后你可以在命令行上键入:
./script.py -i name img.png voids '#00ff00ff' 0 '#ff00ff00' '100%' '#f80654ff'
获得所需的结果:
Namespace(image={'name': 'img.png', '0': '#ff00ff00', 'voids': '#00ff00ff', '100%': '#f80654ff'})
答案 7 :(得分:1)
一般建议:不要使用评估。
如果你真的需要...... “eval”很危险。如果您确定没有人会故意输入恶意输入,请使用它。即使这样,也会有缺点。我已经介绍了一个不好的例子。
使用eval而不是json.loads也有一些优点。一个字典并不一定是一个有效的json。因此,eval在接受“词典”方面可能相当宽松。我们可以通过确保最终结果确实是一个python词典来处理“危险”部分。
import json
import argparse
tests = [
'{"name": "img.png","voids": "#00ff00ff","0": "#ff00ff00","100%": "#f80654ff"}',
'{"a": 1}',
"{'b':1}",
"{'$abc': '$123'}",
'{"a": "a" "b"}' # Bad dictionary but still accepted by eval
]
def eval_json(x):
dicti = eval(x)
assert isinstance(dicti, dict)
return dicti
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=eval_json)
for test in tests:
args = parser.parse_args(['-i', test])
print(args)
输出:
Namespace(input={'name': 'img.png', '0': '#ff00ff00', '100%': '#f80654ff', 'voids': '#00ff00ff'})
Namespace(input={'a': 1})
Namespace(input={'b': 1})
Namespace(input={'$abc': '$123'})
Namespace(input={'a': 'ab'})
答案 8 :(得分:1)
从命令行将参数作为字典传递的最小示例:
# file.py
import argparse
import json
parser = argparse.ArgumentParser()
parser.add_argument("-par", "--parameters",
required=False,
default=None,
type=json.loads
)
args = parser.parse_args()
print(args.parameters)
,在终端中,您可以使用字符串格式将参数作为字典传递:
python file.py --parameters '{"a":1}'
答案 9 :(得分:0)
你可以尝试:
$ ./script.py -i "{'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}"
我现在没有在手机上测试过这个。
编辑: BTW我同意@wim,我认为将dict的每个kv作为参数对用户来说会更好。
答案 10 :(得分:0)
这是另一种解决方案,因为我必须自己做类似的事情。我使用ast
模块将字典(以字符串形式输入到终端)转换成字典。这很简单。
假设以下内容称为test.py
:
import argparse
import ast
parser = argparse.ArgumentParser()
parser.add_argument('--params', '--p', help='dict of params ',type=str)
options = parser.parse_args()
my_dict = options.params
my_dict = ast.literal_eval(my_dict)
print(my_dict)
for k in my_dict:
print(type(my_dict[k]))
print(k,my_dict[k])
然后在终端/ cmd行中输入:
python test.py --p '{"name": "Adam", "lr": 0.001, "betas": (0.9, 0.999)}'
{'name': 'Adam', 'lr': 0.001, 'betas': (0.9, 0.999)}
<class 'str'>
name Adam
<class 'float'>
lr 0.001
<class 'tuple'>
betas (0.9, 0.999)
答案 11 :(得分:0)
TLDR 解决方案: 最简单快捷的解决方案如下:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-par", "--parameters",
default={},
type=str)
args = parser.parse_args()
在 parser.add_argument
函数中:
str
作为类型然后 args.parameters
将自动转换为字典,无需 ast.literal.eval
或 json.loads
。
动机:
@Galuoises 和 @frankeye 发布的方法在 default
设置为 json 编码字典时似乎不起作用,如下所示。
parser.add_argument("-par", "--parameters",
required=False, default="{\"k1\":v1, \"k2\":v2}",
type=json.loads)
这是因为
答案 12 :(得分:0)
以下工作正常:
parser = argparse.ArgumentParser()
parser.add_argument("-par", "--parameters",
required=False, default={"k1a":"v1a","k2a":"v2a"},
type=json.loads)
args = parser.parse_args()
print(str(parameters))
result:
{'k1a': 'v1a', 'k2a': 'v2a'}
对于默认值,类型应该是dict,因为json.loads返回的是字典,而不是字符串,默认对象应该是字典。
import argparse,json,sys
sys.argv.extend(['-par','{"k1b":"v1b","k2b":"v2b"}'])
parser = argparse.ArgumentParser()
parser.add_argument("-par", "--parameters",
required=False, default={"k1":"v1","k2":"v2"},
type=json.loads)
args = parser.parse_args()
print(str(args.parameters))
result:
{'k1b': 'v1b', 'k2b': 'v2b'}