Python 使用argparse.ArgumentParser()获取分组结构

Python 使用argparse.ArgumentParser()获取分组结构,python,argparse,Python,Argparse,我想翻译如下内容: test.py --group 1 --opt1 foo1 --opt2 bar1 \ --group 2 --opt1 foo2 \ --group 3 --opt1 foo3 --opt2 bar3 --opt3 baz3 类似于字典的内容(如命名空间): 如果需要,输入可以是另一种格式 如果使用的不是(opt1、opt2、opt3),我想引发一个错误 我应该使用argparse.ArgumentParser() 你能帮忙吗?这是我能

我想翻译如下内容:

test.py --group 1 --opt1 foo1 --opt2 bar1 \
        --group 2 --opt1 foo2 \
        --group 3 --opt1 foo3 --opt2 bar3 --opt3 baz3
类似于字典的内容(如命名空间):

  • 如果需要,输入可以是另一种格式
  • 如果使用的不是(opt1、opt2、opt3),我想引发一个错误
  • 我应该使用argparse.ArgumentParser()

你能帮忙吗?

这是我能想到的最好的,希望这就是你想要的

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

也就是说,使用
python3script.py--group1=2=3--opt1foo1=foo2=foo3--opt2bar1=None=bar3--opt3none=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': {'opt1': 'foo1', 'opt2': 'bar1'}, '2': {'opt1': 'foo2'}, '3': {'opt2': 'bar3', 'opt3': 'baz3', 'opt1': 'foo3'}}
  • 如果需要,输入可以是另一种格式(检查)

    使用与python3 script.py类似的格式--group1=2=3--opt1foo1=foo2=foo3--opt2bar1=None=bar3--opt3none=None=baz3

  • 如果使用的不是(opt1、opt2、opt3),我想引发一个错误(检查)

    默认情况下,这将使用
    argparse
    ,您可以改为使用
    --opt4
    进行尝试

  • 我应该使用argparse.ArgumentParser()(检查)


  • 这似乎是一个很酷的问题--
    argparse
    允许您以两种方式扩展其回调,一种是使用自定义类型的
    type=…
    ,另一种是使用自定义行为的
    action=…
    。我认为
    type=
    不可能出现上述问题,因此我采用了一种涉及
    action=

    编写自定义操作的基本方法包括从
    argparse.action
    继承和重写
    \uuuuuu调用\uuuuuuuuuuu
    (以及可选的
    \uuuuu init\uuuuuu

    这里是实现您想法的开始,您可能需要对其进行扩展/更改(例如,在action类中调用
    super()。\uuuu call\uuu(…)
    ,以处理基本行为(对于类型等)——我只使用了最简单的方法来满足您的问题

    import argparse
    导入pprint
    类GroupAction(argparse.Action):
    def uu调用(self、解析器、命名空间、值、选项u string=None):
    组,=值
    名称空间。\当前\组=组
    groups=namespace.\uuuu dict\uuuu.setdefault('groups',{})
    如果分组:
    raise argparse.ArgumentError(self,f'已指定:{group}')
    组[组]={}
    类AppendToGroup(argparse.Action):
    def uu调用(self、解析器、命名空间、值、选项u string=None):
    值,=值
    如果不是getattr(命名空间“\u当前\u组”,无):
    raise argparse.ArgumentError(self,“组外!”)
    namespace.groups[namespace.\u current\u group][self.dest]=值
    parser=argparse.ArgumentParser()
    parser.add_参数('--group',action=GroupAction)
    add_参数('--opt1',action=AppendToGroup)
    parser.add_参数('--opt2',action=AppendToGroup)
    parser.add_参数('--opt3',action=AppendToGroup)
    pprint.pprint(parser.parse_args().groups)
    
    这里请注意,我使用
    名称空间
    来存储状态,因此不幸的是,最后您将得到一个额外的
    参数

    \uuuuu dict\uuuuu.setdefault
    有点狡猾,所以我会解释一下——这是一种略短的书写方式:

    如果不是hasattr(名称空间,'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!
    

    到目前为止,您做了些什么?查看此内容了解如何提问:我想也是使用外部配置…?每次调用只能有一个
    group
    ,或多个?如果有多个组,您可能希望使用命令行值以外的其他值,例如
    yaml
    json
    文件。只需在命令行中提供文件名。@hpaulj是的,在我的回答中没有格式输入似乎是不可能的。如果需要的话,OP看起来很好。如果你要否决我的答案,请考虑让我知道为什么。我花了很多时间在这上面,并且希望知道我哪里出了错。l、 我喜欢使用额外的名称空间属性作为“state”变量,或者在操作之间进行通信。