2011-10-02 85 views
15

我想设置一个字典作为可选参数(使用argparse);下面一行是我到目前为止有:type = dict in argparse.add_argument()

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

您可以从外部文件或标准输入读取的格式,如JSON,然后解析它。所以你argparse类型实际上是一个文件。 –

+0

@wim在他的回答中说shell在将它们传递给python之前正在处理这些参数。如果你用'echo'('echo ./script.py -i {'name':...'')你会看到什么python看到(主要是它没有收到任何报价)。在你的情况下,你的param中没有'$'(可以被shell解释为一个环境变量),你可以用双引号括住你的dict:'./script.py -i“{'name':' img.png',....}“' –

回答

4

我敢打赌,你的壳是福> < 0ring大括号(见here)。我只需在CLI中使用参数Argument groups逐个传递参数,然后以编程方式构建字典。

传递一个像字典这样复杂的python对象,迫使用户知道python语法,对我来说似乎有点让人难过。

0

你可以尝试:

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

我没有测试过这一点,我的手机上现在。

编辑: BTW我同意@wim,我觉得具有字典的每个KV作为一个参数将是用户更好的。

1

可以在东西看起来像字面字典入参数解析器肯定搞定,但你因此当外壳解析您的命令行引用它,它就会出现,

  • 单参数,而不是很多(空格字符是正常的参数分隔符)
  • 正确引用(壳解析过程中删除引号,因为它使用它们分组)

因此,像这样可以得到你想要的文字进入你的p rogram:

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'} 
+0

哇,感谢您的可能性概述;然而,尽管在这个例子中我只写了0和100%,但它们实际上可以是任何值(例如{'46%':'#0f0e0d0c','3629','#f0e0d0c0'})在你最后一段代码中...... – user975296

37

Necroing这样: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'}

+0

'json.loads'是'type'的不错选择。就像'int'和'float'一样,它需要一个字符串,并且如果它无法处理则返回一个'ValueError'。它比'eval'更安全。为此,它可能有点过于笼统(即它可以处理一个''[1,2]'')列表,但是用户可以在'parse_args()'后处理。 – hpaulj

+0

当这些值是'str'ings,'int','float'时,它工作正常。对于其他类型的值,例如'bool',它不会(但是将'1'传递给'True'应该可以工作,但所有代码都是正确的)。 – gerrit

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

为了完整,同样以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 barfing。如果您需要数字而不是字符串值,则使用以下数据似乎可以很好地工作:

>>> 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} 
+0

更正(缺少)parser.parse_args调用。感谢您指出这一点,Hotschke – Hamish

0

一般建议:请勿使用eval。

如果你真的要... “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'}) 
+0

这是非常危险的建议。使用eval将(显然)导致来自cmdline的输入被评估为python。这发生在*它返回值之前,所以你的类型检查太迟了。此外,还有大量有效的危险python,它仍然会返回一个字典...... “我们可以照顾危险”声明既不准确又危险,可以作为建议传播。 –

+0

好的。同意。我正在改变答案的语言。 –