使用获取分组结构argparse.ArgumentParser参数分析器()

2024-09-29 01:32:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我想翻译如下:

test.py --group 1 --opt1 foo1 --opt2 bar1 \
        --group 2 --opt1 foo2 \
        --group 3 --opt1 foo3 --opt2 bar3 --opt3 baz3

比如字典(比如命名空间):

{
  "1": {"opt1": "foo1", 
        "opt2": "bar1"},
  "2": {"opt1": "foo2"}, 
  "3": {"opt1": "foo3", 
        "opt2": "bar3",
        "opt3": "baz3"}
}
  • 如果需要,输入可以是另一种格式。你知道吗
  • 如果使用的不是(opt1,opt2,opt3),我想引发一个错误
  • 我应该用argparse.ArgumentParser参数分析器(). 你知道吗

你能帮忙吗?你知道吗


Tags: pytest字典group空间命名foo1foo2
2条回答

似乎是一个很酷的问题argparse允许您以两种方式扩展它的回调,一种是针对自定义类型的type=...,另一种是针对自定义行为的action=...。我认为上述问题在type=中是不可能的,所以我使用了一个涉及action=的解决方案

编写自定义操作的基本方法包括从argparse.Action继承和重写__call__(以及可选的__init__

下面是实现您的想法的一个开始,您可能需要扩展/更改它(例如,在action类中调用super().__call__(...)以处理基本行为(对于类型等)可能是有意义的)。我只使用了最简单的方法来满足您的问题。你知道吗

import argparse
import pprint


class GroupAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        group, = values
        namespace._current_group = group
        groups = namespace.__dict__.setdefault('groups', {})
        if group in groups:
            raise argparse.ArgumentError(self, f'already specified: {group}')
        groups[group] = {}


class AppendToGroup(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        value, = values
        if not getattr(namespace, '_current_group', None):
            raise argparse.ArgumentError(self, 'outside of group!')
        namespace.groups[namespace._current_group][self.dest] = value


parser = argparse.ArgumentParser()
parser.add_argument(' group', action=GroupAction)
parser.add_argument(' opt1', action=AppendToGroup)
parser.add_argument(' opt2', action=AppendToGroup)
parser.add_argument(' opt3', action=AppendToGroup)
pprint.pprint(parser.parse_args().groups)

注意,这里我使用namespace来存储状态,所以不幸的是,最后你会得到一点额外的args._current_group。你知道吗

__dict__.setdefault有点鬼鬼祟祟,所以我来解释一下,这是一种略短的写作方式:

if not hasattr(namespace, 'groups'):
    namespace.groups = {}
groups = namespace.groups

我使用的基本策略是存储当前组,并在看到其他参数时附加到该组

以下是实际操作的示例:

$ python3.7 t.py  group 1  opt1 a  opt2 b  opt3 c
{'1': {'opt1': 'a', 'opt2': 'b', 'opt3': 'c'}}
$ python3.7 t.py  group 1  opt1 a  opt2 b  opt3 c  group 2
{'1': {'opt1': 'a', 'opt2': 'b', 'opt3': 'c'}, '2': {}}
$ python3.7 t.py  group 1  opt1 a  opt2 b  opt3 c  group 2  opt3 c
{'1': {'opt1': 'a', 'opt2': 'b', 'opt3': 'c'}, '2': {'opt3': 'c'}}
$ python3.7 t.py  group 1  group 1
usage: t.py [-h] [ group GROUP] [ opt1 OPT1] [ opt2 OPT2] [ opt3 OPT3]
t.py: error: argument  group: already specified: 1
$ python3.7 t.py  opt1 a  group 1
usage: t.py [-h] [ group GROUP] [ opt1 OPT1] [ opt2 OPT2] [ opt3 OPT3]
t.py: error: argument  opt1: outside of group!

这是我能想到的最好的,希望这就是你想要的。你知道吗

您需要以代码底部显示的形式提供参数,但您在文章中提到这是可以接受的。你知道吗

也就是说,使用python3 script.py group 1=2=3 opt1 foo1=foo2=foo3 opt2 bar1=None=bar3 opt3 None=None=baz3来重现我的结果。你知道吗

import argparse, copy
parser = argparse.ArgumentParser()
parser.add_argument(" group", default='1')
parser.add_argument(" opt1", default='foo1')
parser.add_argument(" opt2", default='bar1')
parser.add_argument(" opt3", default='baz3')
args = parser.parse_args()

def splitter(arg):
    return arg.split("=")

groups = splitter(args.group) if "=" in args.group else args.group
opt1 = splitter(args.opt1) if "=" in args.opt1 else args.opt1
opt2 = splitter(args.opt2) if "=" in args.opt2 else args.opt2
opt3 = splitter(args.opt3) if "=" in args.opt3 else args.opt3

# Dictionary with no values corresponding to just the keys in "group"
group_dict = dict.fromkeys(groups)
final_dict = copy.deepcopy(group_dict)

# Add dictionary inside each key - will remove blank keys later.
for key in group_dict:
    group_dict[key] = dict.fromkeys(['opt1', 'opt2', 'opt3'])

# Now each group_dict has form
# {'3': {'opt2': None, 'opt3': None, 'opt1': None}, '2': {'opt2': None, 'opt3': None, 'opt1': None}, '1': {'opt2': None, 'opt3': None, 'opt1': None}}
# Must use group_dict['1']['opt3'] to access 1 -> opt3 -> value
for i, key in enumerate(groups):
    group_dict[key]["opt1"] = opt1[i]
    group_dict[key]["opt2"] = opt2[i]
    group_dict[key]["opt3"] = opt3[i]

    # Remove None elements - key is main dict - key2 & value are sub dict
    final_dict[key] = {key2: value for key2, value in group_dict[key].items() if value != 'None'}

print(final_dict)

输出:

{'1': {'opt1': 'foo1', 'opt2': 'bar1'}, '2': {'opt1': 'foo2'}, '3': {'opt2': 'bar3', 'opt3': 'baz3', 'opt1': 'foo3'}}

摘要:

  1. 如果需要,输入可以是另一种格式。(检查)

    使用与python3 script.py group 1=2=3 opt1 foo1=foo2=foo3 opt2 bar1=None=bar3 opt3 None=None=baz3

    类似的格式
  2. 如果使用的不是(opt1,opt2,opt3),我想引发一个错误。(检查)

    这将在默认情况下使用argparse实现,您可以尝试使用 opt4

  3. 我应该用argparse.ArgumentParser参数分析器(). (检查)

相关问题 更多 >