argparse.add_argument() 中的 type=dict

发布于 2024-12-07 18:49:47 字数 766 浏览 1 评论 0 原文

我想使用标准库 argparse 模块来解析我的程序的命令行参数,并让程序接受可选参数 -i (或 --image )这是一本字典。

我尝试像这样配置解析器:

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

我应该如何a)编写命令行和b)设置 argparse 逻辑?

I want to use the standard library argparse module to parse command line arguments to my program, and have the program accept an optional argument -i (or --image) which is a dictionary.

I tried configuring the parser like this:

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\'}).')

But when I try to run the script, I get an error:

$ ./script.py -i {'name': 'img.png','voids': '#00ff00ff','0': '#ff00ff00','100%': '#f80654ff'}
    
script.py: error: argument -i/--image: invalid dict value: '{name:'

Even though similar syntax would work fine inside the interpreter:

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

How should I a) write the command line and b) set up the argparse logic?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

铜锣湾横着走 2024-12-14 18:49:47

破坏这个: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 '}

Necroing this: json.loads works here, too. It doesn't seem too dirty.

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)

Returns:

{u'0': u'#ff00ff00', u'100%': u'#f80654ff', u'voids': u'#00ff00ff', u'name': u'img.png'}

乜一 2024-12-14 18:49:47

为了完整起见,与 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}

For completeness, and similarly to json.loads, you could use yaml.load (available from PyYAML in PyPI). This has the advantage over json in that there is no need to quote individual keys and values on the command line unless you are trying to, say, force integers into strings or otherwise overcome yaml conversion semantics. But obviously the whole string will need quoting as it contains spaces!

>>> 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

Although admittedly in the question given, many of the keys and values would have to be quoted or rephrased to prevent yaml from barfing. Using the following data seems to work quite nicely, if you need numeric rather than string values:

>>> 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}
情未る 2024-12-14 18:49:47

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

Using simple lambda parsing is quite flexible:

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'
)
转瞬即逝 2024-12-14 18:49:47

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

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

I’ll bet your shell is messing with the braces, since curly braces are the syntax used for brace expansion features in many shells (see here).

Passing in a complex container such as a dictionary, requiring the user to know Python syntax, seems a bad design choice in a command line interface. Instead, I’d recommend just passing options in one-by-one in the CLI within an argument group, and then build the dict programmatically from the parsed group.

哭泣的笑容 2024-12-14 18:49:47

结合 @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

Combining the type= piece from @Edd and the ast.literal_eval piece from @Bradley yields the most direct solution, IMO. It allows direct retrieval of the argval and even takes a (quoted) default value for the dict:

Code snippet

parser.add_argument('--params', '--p', help='dict of params ', type=ast.literal_eval, default="{'name': 'adam'}")
args = parser.parse_args()

Running the Code

python test.py --p "{'town': 'union'}"

note the quotes on the dict value. This quoting works on Windows and Linux (tested with [t]csh).

Retrieving the Argval

dict=args.params
携余温的黄昏 2024-12-14 18:49:47

你绝对可以将看起来像字典文字的东西输入到参数解析器中,但是你必须引用它,这样当 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'}

You can definitely get in something that looks like a dictionary literal into the argument parser, but you've got to quote it so when the shell parses your command line, it comes in as

  • a single argument instead of many (the space character is the normal argument delimiter)
  • properly quoted (the shell removes quotes during parsing, because it's using them for grouping)

So something like this can get the text you wanted into your program:

python MYSCRIPT.py -i "{\"name\": \"img.png\", \"voids\": \"#00ff00ff\",\"0\": \"#ff00ff00\",\"100%\": \"#f80654ff\"}"

However, this string is not a valid argument to the dict constructor; instead, it's a valid python code snippet. You could tell your argument parser that the "type" of this argument is eval, and that will work:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i','--image', type=eval, help='Generate an image map...')
args = parser.parse_args()
print args

and calling it:

% python MYSCRIPT.py -i "{\"name\": \"img.png\", \"voids\": \"#00ff00ff\",\"0\": \"#ff00ff00\",\"100%\": \"#f80654ff\"}"
Namespace(image={'0': '#ff00ff00', '100%': '#f80654ff', 'voids': '#00ff00ff', 'name': 'img.png'})

But this is not safe; the input could be anything, and you're evaluating arbitrary code. It would be equally unwieldy, but the following would be much safer:

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

This also works, but is MUCH more restrictive on what it will allow to be eval'd.

Still, it's very unwieldy to have the user type out something, properly quoted, that looks like a python dictionary on the command line. And, you'd have to do some checking after the fact to make sure they passed in a dictionary instead of something else eval-able, and had the right keys in it. Much easier to use if:

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

For:

% 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'}
夏尔 2024-12-14 18:49:47

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

One of the simplest ways I've found is to parse the dictionary as a list, and then convert that to a dictionary. For example using 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)

then you can type on the command line something like:

./script.py -i name img.png voids '#00ff00ff' 0 '#ff00ff00' '100%' '#f80654ff'

to get the desired result:

Namespace(image={'name': 'img.png', '0': '#ff00ff00', 'voids': '#00ff00ff', '100%': '#f80654ff'})
酷到爆炸 2024-12-14 18:49:47

从命令行将参数作为字典传递的最小示例:

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

A minimal example to pass arguments as a dictionary from the command line:

# 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)

and in the terminal you can pass your arguments as a dictionary using a string format:

python file.py --parameters '{"a":1}'
倾城花音 2024-12-14 18:49:47

一般建议:不要使用 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'})

General Advice: DO NOT USE eval.

If you really have to ...
"eval" is dangerous. Use it if you are sure no one will knowingly input malicious input. Even then there can be disadvantages. I have covered one bad example.

Using eval instead of json.loads has some advantages as well though. A dict doesn't really need to be a valid json. Hence, eval can be pretty lenient in accepting "dictionaries". We can take care of the "danger" part by making sure that final result is indeed a python dictionary.

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)

Output:

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'})
十二 2024-12-14 18:49:47

 您可以尝试:

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

我还没有在我的手机上测试过这个。

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

 You could try:

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

I haven't tested this, on my phone right now.

Edit: BTW I agree with @wim, I think having each kv of the dict as an argument would be nicer for the user.

够钟 2024-12-14 18:49:47

这是另一个解决方案,因为我自己也必须做类似的事情。我使用 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)

Here is a another solution since I had to do something similar myself. I use the ast module to convert the dictionary, which is input to the terminal as a string, to a dict. It is very simple.

Code snippet

Say the following is called 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])

Then in the terminal/cmd line, you would write:

Running the code

python test.py --p '{"name": "Adam", "lr": 0.001, "betas": (0.9, 0.999)}'

Output

{'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)
多孤肩上扛 2024-12-14 18:49:47

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

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

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

这是因为

TLDR Solution:
The simplest and quickest solution is as below:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-par", "--parameters",
                    default={},
                    type=str)
args = parser.parse_args()

In the parser.add_argument function:

  1. Use a dictionary object for default object
  2. str as the type

Then args.parameters will automatically be converted to a dictionary without any need for ast.literal.eval or json.loads.

Motivation:
The methods posted by @Galuoises and @frankeye, appear to not work when the default is set as a json encoded dictionary such as below.

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

This is because

谷夏 2024-12-14 18:49:47

以下工作正常:

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

The following works just fine:

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

For default value, the type should be dict since json.loads returns a dictionary, not a string, the default object should be given as a dictionary.

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'}
与酒说心事 2024-12-14 18:49:47

就我而言,我需要传递具有多个值的标签列表,并且我想将它们作为字典获取。所以我结合了 nargs 和 Action 来做到这一点:

class LabelAction(argparse.Action):
    def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace,
                 values: Union[str, Sequence[Any], None],
                 option_string: Optional[str] = ...) -> None:
        labels = getattr(namespace, self.dest, None) or {}
        labels[values[0]] = values[1:]
        setattr(namespace, self.dest, labels)

parser = argparse.ArgumentParser(description='Labels dict')
parser.add_argument('-l', nargs='*', action=LabelAction)

参数行:

-l label1 v1 v2 v3 -l label2 x1 x10

带来了字典:

{ 
  'label1': ['v1', 'v2', 'v3'],
  'label2': ['x1', 'x10'],
}

In my case I need to pass list of labels with multiple values and I wanted to get them as dictionary. So I did it with combination of nargs and Action:

class LabelAction(argparse.Action):
    def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace,
                 values: Union[str, Sequence[Any], None],
                 option_string: Optional[str] = ...) -> None:
        labels = getattr(namespace, self.dest, None) or {}
        labels[values[0]] = values[1:]
        setattr(namespace, self.dest, labels)

parser = argparse.ArgumentParser(description='Labels dict')
parser.add_argument('-l', nargs='*', action=LabelAction)

And the line of arguments:

-l label1 v1 v2 v3 -l label2 x1 x10

brings the dictionary:

{ 
  'label1': ['v1', 'v2', 'v3'],
  'label2': ['x1', 'x10'],
}
潦草背影 2024-12-14 18:49:47

我意识到这是一个古老的问题,但没有一个答案能解决我的具体情况。如果我错过了一些明显的事情,请提前道歉,但我的 Python 编码经验不到 7 天。

我的例子是一本字典,其中的值本身可以是字典。例如下面的简单示例:

{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}

由于字典的字典似乎是一个合法的构造,因此我认为将我可能的解决方案添加到已经很长的解决方案列表中是有意义的。


这种情况没有由 json 解决方案处理,如下所示:

import json
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=json.loads)

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

返回:

usage: [-h] [-i INPUT]
: error: argument -i/--input: invalid loads value: "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"

这种情况没有由 yaml 解决方案处理,如下所示:

import yaml
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=yaml.load)

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

返回:

usage: [-h] [-i INPUT]
: error: argument -i/--input: invalid load value: "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"

我发现 ast.literal_eval 解决方案确实有效,但未能实际检查它传递了一个字典,如下所示:

import ast
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=ast.literal_eval)

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))


test = "{'a', 'b', 'c'}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))

返回:

{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}
<class 'dict'>

{'a', 'b', 'c'}
<class 'set'>

据我了解,add_argument() 的“type”参数的要点是对该参数进行类型检查。我不会声称以下解决方案是防弹的,但我确实相信它可以正确处理字典的字典情况,并且如果将字典以外的内容传递给它,则应该返回用法。对之前的答案的所有应有的尊重,因为我只能根据我对与使用 lambda 和 ast.literal_eval() 相关的两个先前答案的理解和组合来将其拼凑在一起。我希望它对我以外的人有用。

import ast
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=lambda x: dict(ast.literal_eval(x)))

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))


test = "{'a', 'b', 'c'}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))

返回:

{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}
<class 'dict'>

usage: [-h] [-i INPUT]
: error: argument -i/--input: invalid <lambda> value: "{'a', 'b', 'c'}"

I realize this is an ancient question, but none of the answers handled my specific case. Apologies in advance if I missed something obvious, but my python coding experience consists of less than 7 days.

My case was a dictionary where the values could themselves be dictionaries. Such as the following simple example:

{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}

Since a dictionary of dictionaries seems to be a legal construct, I thought it made sense to add my possible solution to the already long list of solutions.


This case wasn't handled by the json solution as shown below:

import json
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=json.loads)

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

Returned:

usage: [-h] [-i INPUT]
: error: argument -i/--input: invalid loads value: "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"

This case wasn't handled by the yaml solution as shown below:

import yaml
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=yaml.load)

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

Returned:

usage: [-h] [-i INPUT]
: error: argument -i/--input: invalid load value: "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"

I found that the ast.literal_eval solution did work, but failed to actually check that it was passed a dictionary as shown below:

import ast
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=ast.literal_eval)

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))


test = "{'a', 'b', 'c'}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))

Returns:

{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}
<class 'dict'>

{'a', 'b', 'c'}
<class 'set'>

As I understood it, the point of "type" argument to add_argument() was to do type checking for that argument. I will not claim that the following solution is bullet proof, but I do believe that it correctly handles the dictionary of dictionaries case and should return usage if something other than a dictionary is passed to it. All due respect to previous answers because I was only able to piece this together from my understand of, and the combining of, two previous answers related to using lambda and ast.literal_eval(). I hope it is useful to someone other than me.

import ast
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', type=lambda x: dict(ast.literal_eval(x)))

test = "{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))


test = "{'a', 'b', 'c'}"
args = parser.parse_args(['-i', test])

print(args.input)
print(type(args.input))

Returns:

{'a': {'x': 1}, 'b': {'y': 2}, 'c': {'z': 3}}
<class 'dict'>

usage: [-h] [-i INPUT]
: error: argument -i/--input: invalid <lambda> value: "{'a', 'b', 'c'}"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文