36

我正在尝试将字典设置为可选参数(使用 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'}

工作得很好。

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

4

13 回答 13

62

消除这个: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'}

于 2013-08-01T20:29:54.967 回答
15

为了完整起见,与 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}
于 2013-12-10T11:28:49.027 回答
7

使用简单的 lambda 解析非常灵活:

parser.add_argument(
    '--fieldMap',
    type=lambda x: {k:int(v) for k,v in (i.split(':') for i in x.split(','))},
    help='comma-separated field:position pairs, e.g. Date:0,Amount:2,Payee:5,Memo:9'
)
于 2020-04-08T06:29:21.560 回答
6

我敢打赌你的 shell 弄乱了大括号,因为花括号是许多 shell 中用于大括号扩展功能的语法(请参阅此处)。

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

于 2011-10-02T10:21:58.447 回答
4

type=来自@Edd 的ast.literal.eval部分和来自@Bradley 的部分结合起来产生最直接的解决方案,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
于 2020-08-28T02:12:47.367 回答
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

尽管如此,让用户在命令行上输入一些看起来像 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'}
于 2011-10-02T15:28:15.917 回答
2

我发现的最简单的方法之一是将字典解析为列表,然后将其转换为字典。例如使用 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'})
于 2013-12-05T13:53:55.197 回答
1

一般建议:不要使用 eval。

如果你真的必须...... “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'})
于 2016-09-06T20:30:43.833 回答
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}'
于 2020-11-17T11:33:31.470 回答
0

 你可以试试:

$ ./script.py -i "{'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}"

我还没有测试过,现在在我的手机上。

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

于 2011-10-02T10:54:17.177 回答
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)
于 2019-09-05T19:13:17.273 回答
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.evalor json.loads

动机default:@Galuoises 和@frankeye 发布的方法在设置为如下所示的 json 编码字典时似乎不起作用。

parser.add_argument("-par", "--parameters",
                required=False,  default="{\"k1\":v1, \"k2\":v2}",
                type=json.loads)

这是因为

于 2020-12-19T00:03:22.353 回答
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'}
于 2021-08-07T01:12:59.087 回答