argparse.add_argument()中的type = dict

时间:2011-10-02 10:17:20

标签: python 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\'}).')

但是运行脚本:

 $ ./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'}

工作正常。

那么我应该如何传递参数呢? 提前谢谢。

13 个答案:

答案 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解析你的命令行时,它会以

形式出现。
  • 单个参数而不是多个(空格字符是普通参数分隔符)
  • 正确引用(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测试)上有效。

获取Argval

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 函数中:

  1. 使用字典对象作为默认对象
  2. str 作为类型

然后 args.parameters 将自动转换为字典,无需 ast.literal.evaljson.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'}