Python type=argparse.add_参数()中的dict

Python type=argparse.add_参数()中的dict,python,dictionary,argparse,Python,Dictionary,Argparse,我试图设置一个字典作为可选参数(使用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\'}).

我试图设置一个字典作为可选参数(使用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\'}).')
即使在口译员内部

>>> a={'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}
很好用

那么我应该如何通过这个论点呢?
提前感谢。

我敢打赌,您的shell会弄乱大括号,因为大括号是许多shell中用于大括号扩展功能的语法(请参阅)

在命令行界面中,传入一个复杂的容器(如字典),要求用户了解Python语法,这似乎是一个糟糕的设计选择。相反,我建议只在中的CLI中逐个传递选项,然后从已解析的组以编程方式构建dict

您可以尝试:

$ ./script.py -i "{'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}"
我还没有在手机上测试过这个


编辑:顺便说一句,我同意@wim,我认为将dict的每一个kv作为参数对用户来说会更好。

您肯定可以在参数解析器中输入一些看起来像字典文字的内容,但您必须引用它,因此当shell解析您的命令行时,它会作为

  • 单个参数而不是多个(空格字符是正常的参数分隔符)
  • 正确引用(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
用于:


在这里,
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'}

我发现的最简单的方法之一是将字典解析为列表,然后将其转换为字典。例如,使用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'})

为了完整性,并且与json.load类似,您可以使用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}

一般建议:不要使用eval

如果你真的必须… “评估”是危险的。如果您确定没有人会故意输入恶意输入,请使用它。即使这样,也可能有缺点。我讲了一个坏例子

不过,使用eval而不是json.loads也有一些优点。dict实际上不需要是有效的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'})

这是另一个解决方案,因为我必须自己做类似的事情。我使用
ast
模块将字典(作为字符串输入到终端)转换为dict。这非常简单

代码片段 假设以下内容称为
test.py

import argparse
导入ast
parser=argparse.ArgumentParser()
parser.add_参数('--params','--p',help='dict of params',type=str)
options=parser.parse_args()
my_dict=options.params
my_dict=ast.literal_eval(my_dict)
打印(我的字典)
在我的演讲中,对于k:
打印(键入(my_dict[k]))
打印(k,我的字典[k])
然后在terminal/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)}
叫亚当
lr 0.001
贝塔(0.9,0.999)

使用简单的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'
)

IMO,将@Edd中的
type=
片段与@Bradley中的
ast.literal.eval
片段相结合,可以产生最直接的解决方案。它允许直接检索argval,甚至可以为dict取一个(引用的)默认值:

代码片段 运行代码 注意dict值上的引号。此引用适用于Windows和Linux(使用[t]csh测试)

检索Argval
从命令行将参数作为字典传递的最简单示例:

# 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}'

TLDR解决方案: 最简单、最快捷的解决方案如下:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-par", "--parameters",
                    default={},
                    type=str)
args = parser.parse_args()
解析器中,添加参数
函数:

  • 使用字典对象作为默认对象
  • str
    作为类型
  • 然后,
    args.parameters
    将自动转换为字典,而不需要
    ast.literal.eval
    json.loads

    动机: 当
    默认值为时,@Galuoises和@frankeye发布的方法似乎不起作用
    
    python test.py --p "{'town': 'union'}"
    
    dict=args.params
    
    # 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}'
    
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("-par", "--parameters",
                        default={},
                        type=str)
    args = parser.parse_args()
    
    parser.add_argument("-par", "--parameters",
                    required=False,  default="{\"k1\":v1, \"k2\":v2}",
                    type=json.loads)