Python argparse: 复杂参数解析场景

5
我希望实现以下命令行参数解析场景:
我有四个参数:-g-wid-w1-w2-w1-w2 总是一起出现。 -wid(-w1 -w2) 是互斥的,但其中一个是必须的。 -g 是可选的;如果没有指定,只能出现 (-w1 -w2),而不是 -wid
是否有一种优雅的方法使用 argparse 实现这一点,但不使用子命令(我已经在子命令中了)?
我考虑使用自定义操作,但是在操作体中,我需要知道何时调用它的最后一次(即正在解析最后一个参数时),但由于参数的数量和顺序可能会变化,我不知道该如何做。
如果需要更多解释:我正在编写的工具使用小部件和小工具参数 -g 创建小工具。小部件可以是现有小部件 - 然后它将通过其 id -wid 来引用,也可以是使用参数 -w1-w2 创建的新小部件。如果未指定 -g,则该工具将只使用 (-w1 -w2) 创建和存储新小部件,而不创建小工具。
提前感谢您。

如果-w1和-w2总是一起出现,你为什么要有两个参数? - mgilson
@mgilson:其实,我一直在思考:只使用nargs=2指定一个参数。然后将(-w1 -w2)和-wid创建为互斥的必需组。但我仍然需要解决最后一个问题。 - malenkiy_scot
你看过docopt库吗? - John Smith Optional
@JohnSmithOptional,没有。它似乎没有与Python 2.7捆绑在一起。 - malenkiy_scot
@mgilson,使用nargs=2指定一个参数证明是有问题的。第一个(较小的)问题是我无法为它们分别命名以供使用/帮助目的;更大的问题是它们是不同类型的:一个是字符串,另一个是文件。 - malenkiy_scot
显示剩余2条评论
1个回答

2
如果你正确选择默认值,你可以在'parse_args'之后的'namespace'中轻松测试参数值的逻辑组合。你也可以在那时发出解析器错误。有一个错误报告要求互相包含的组。我在那里提出了一种添加广义组合测试机制的方法。但它仍然需要你自己进行逻辑测试。 http://bugs.python.org/issue11588 在argparse中添加“必须包含”组
我在这个问题中提出的解决方案的关键是使程序员可以使用seen_non_default_actions变量。这是一个已经看到的操作列表(实际上是一个集合),考虑到可选位置总是被“看到”的。我希望看到更多讨论如何实现包含和排除组合的混合。
你指定:

我有4个参数:-g,-wid,-w1和-w2。 -w1和-w2总是一起出现 -wid和(-w1 -w2)是互斥的,但其中一个是必需的 -g是可选的;如果没有指定,则只能出现(-w1 -w2),但不能出现-wid

我会尝试概括为:
complex_group('g', required_next_exclusive_group('wid', inclusive_group('w1','w2)))

"

w1,w2可以用'--w1w2',nargs=2替换。简单的“mutually_inclusive_group”在这里可以起作用。但是argparse无法处理嵌套组。

widw1w2可以放在必需的mutually_exclusive_group中。

-g需要像if args.g is None and args.wid is None: error()这样的测试。

"
这里有一个脚本,它可以按照您的要求(我想是这样)运行,使用了我在Issue11588中的最新补丁。`act_w1`等是实际的操作对象,可能会出现在`seen_actions`列表中。测试函数已经在子解析器中注册,并在其`parse_know_args()`结束时执行。
parser = argparse.ArgumentParser(usage='custom usage')
sp = parser.add_subparsers(dest='cmd')
sp.required = True
spp = sp.add_parser('cmd1')
act_g = spp.add_argument('-g')
act_wid = spp.add_argument('--wid')
act_w1 = spp.add_argument('--w1')
act_w2 = spp.add_argument('--w2')

@spp.crosstest # decorator to register this function with spp
def test1(spp, seen_actions, *args):
    # seen_actions - list of actions that were seen by parser
    if 1==len({act_w1, act_w2}.intersection(seen_actions)):
        # error if only one of these was seen
        parser.error('-w1 and -w2 always appear together')
@spp.crosstest
def test2(spp, seen_actions, *args):
    # required mutually exclusive wid and (w1,w2 group)
    if act_wid in seen_actions:
        if act_w1 in seen_actions or act_w2 in seen_actions:
            parser.error('-wid and (-w1 -w2) are mutually exclusive')
    elif act_w1 not in seen_actions:
        parser.error('wid or (w1 and w2) required')
@spp.crosstest
def test3(spp, seen_actions, *args):
    # is this the simplest logical way of expressing this?
    if act_g not in seen_actions and act_wid in seen_actions:
        parser.error('not g, so not wid')
args = parser.parse_args()

在这个例子中,我保存并测试操作对象的存在。也可以使用“dest”字符串进行测试。我正在探索使这种测试更直观和用户友好的方法。扩展的装饰器集合似乎是最有前途的。

argparse 中检查参数的主要目的是为了拥有一个漂亮、有帮助和统一的自动错误报告(否则就遵循 EAFP)。手动完成这个过程有点违背初衷。另外,正如我所提到的,我在这里谈论的是子命令,因此 parse_args() 在一个完全不同的模块中被调用。当然,这是可以克服的,但我觉得它有些笨重。 - malenkiy_scot
我使用了自定义操作来解决我的特定问题,但解决方案相对复杂(例如,它涉及在由.add_argument()返回的操作中设置/清除.required),我不太喜欢它。在大多数情况下,您提出的方法是最明智的方法,因此我接受了您的答案。 - malenkiy_scot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接