24

如果我有 arguments '-a', '-b', '-c', '-d',那么add_mutually_exclusive_group()我的程序将不得不使用其中的一个函数。有没有办法将它结合起来,以便程序只接受'-a 999 -b 999'or '-c 999 -d 999'

编辑:添加一个简单的程序更清晰:

>>> parser = argparse.ArgumentParser()
>>> group = parser.add_mutually_exclusive_group()
>>> group.add_argument('-a')
>>> group.add_argument('-b')
>>> group.add_argument('-c')
>>> group.add_argument('-d')

然后才./app.py -a | ./app.py -b | ./app.py -c | ./app.py -d可以调用。是否可以将 argparse 分组排除组,以便仅./app.py -a .. -b .. | ./app.py -c .. -d ..调用?

4

4 回答 4

7

编辑:没关系。因为argparse在调用group.add_argument. 那不会是我的设计选择。如果您迫切需要此功能,可以尝试使用ConflictsOptionParser

# exclusivegroups.py
import conflictsparse

parser = conflictsparse.ConflictsOptionParser()
a_opt = parser.add_option('-a')
b_opt = parser.add_option('-b')
c_opt = parser.add_option('-c')
d_opt = parser.add_option('-d')

import itertools
compatible_opts1 = (a_opt, b_opt)
compatible_opts2 = (c_opt, d_opt)
exclusives = itertools.product(compatible_opts1, compatible_opts2)
for exclusive_grp in exclusives:
    parser.register_conflict(exclusive_grp)


opts, args = parser.parse_args()
print "opts: ", opts
print "args: ", args

因此,当我们调用它时,我们可以看到我们得到了想要的效果。

$ python exclusivegroups.py -a 1 -b 2
opts:  {'a': '1', 'c': None, 'b': '2', 'd': None}
args:  []
$ python exclusivegroups.py -c 3 -d 2
opts:  {'a': None, 'c': '3', 'b': None, 'd': '2'}
args:  []
$ python exclusivegroups.py -a 1 -b 2 -c 3
Usage: exclusivegroups.py [options]

exclusivegroups.py: error: -b, -c are incompatible options.

警告消息不会通知您两者'-a''-b'兼容'-c',但是可以制作更合适的错误消息。较旧的,错误的答案如下。

较旧的编辑: [此编辑是错误的,尽管如果以argparse这种方式工作,这不是一个完美的世界吗?]我以前的答案实际上是不正确的,您应该可以通过为argparse每个互斥选项指定一个组来做到这一点。我们甚至可以用它itertools来概括这个过程。并使其不必显式地输入所有组合:

import itertools
compatible_opts1 = ('-a', '-b')
compatible_opts2 = ('-c', '-d')
exclusives = itertools.product(compatible_opts1, compatible_opts2)
for exclusive_grp in exclusives:
    group = parser.add_mutually_exclusive_group()
    group.add_argument(exclusive_grp[0])
    group.add_argument(exclusive_grp[1])
于 2011-01-22T21:31:44.883 回答
5

我自己偶然发现了这个问题。从我对 argparse 文档的阅读来看,似乎没有一种简单的方法可以在 argparse 中实现这一点。我考虑过使用 parse_known_args,但这很快就相当于编写了一个特殊用途的 argparse 版本;-)

也许一个错误报告是为了。同时,如果你愿意让你的用户做一点额外的输入,你可以用子组来伪造它(比如 git 和 svn 的参数是如何工作的),例如

    subparsers = parser.add_subparsers()
    p_ab = subparsers.add_parser('ab')
    p_ab.add_argument(...)

    p_cd = subparsers.add_parser('cd')
    p_cd.add_argument(...)

不理想,但至少它为您提供了 argparse 的好处,而没有太多丑陋的骇客。我最终取消了开关,只使用带有所需子参数的子解析器操作。

于 2012-11-20T15:04:59.910 回答
3

argparse@hpaulj 的评论中提到的增强请求在九年多之后仍然开放,所以我认为其他人可能会从我刚刚发现的解决方法中受益。基于增强请求中的此评论,我发现我能够使用以下语法向两个不同的互斥组添加一个选项:

#!/usr/bin/env python                                                                                                                                                                     
import argparse
import os
import sys

def parse_args():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )

    parser.add_argument("-d", "--device", help="Path to UART device", default="./ttyS0")

    mutex_group1 = parser.add_mutually_exclusive_group()
    mutex_group2 = parser.add_mutually_exclusive_group()

    mutex_group1.add_argument(
        "-o",
        "--output-file",
        help="Name of output CSV file",
        default="sensor_data_sent.csv",
    )

    input_file_action = mutex_group1.add_argument(
        "-i", "--input-file", type=argparse.FileType("r"), help="Name of input CSV file"
    )

    # See: https://bugs.python.org/issue10984#msg219660
    mutex_group2._group_actions.append(input_file_action)

    mutex_group2.add_argument(
        "-t",
        "--time",
        type=int,
        help="How long to run, in seconds (-1 = loop forever)",
        default=-1,
    )

    # Add missing ']' to usage message
    usage = parser.format_usage()
    usage = usage.replace('usage: ', '')
    usage = usage.replace(']\n', ']]\n')
    parser.usage = usage

    return parser.parse_args()


if __name__ == "__main__":
    args = parse_args()
    print("Args parsed successfully...")
    sys.exit(0)

这对我的目的来说足够好:

$ ./fake_sensor.py -i input.csv -o output.csv                                                                                                                                             
usage: fake_sensor.py [-h] [-d DEVICE] [-o OUTPUT_FILE | [-i INPUT_FILE | -t TIME]]
fake_sensor.py: error: argument -o/--output-file: not allowed with argument -i/--input-file

$ ./fake_sensor.py -i input.csv -t 30         
usage: fake_sensor.py [-h] [-d DEVICE] [-o OUTPUT_FILE | [-i INPUT_FILE | -t TIME]]
fake_sensor.py: error: argument -t/--time: not allowed with argument -i/--input-file

$ ./fake_sensor.py -i input.csv
Args parsed successfully...

$ ./fake_sensor.py -o output.csv
Args parsed successfully...

$ ./fake_sensor.py -o output.csv -t 30
Args parsed successfully...

当然,访问私有成员argparse是相当脆弱的,所以我可能不会在生产代码中使用这种方法。此外,精明的读者可能会注意到使用消息具有误导性,因为它暗示-o并且-i可以在它们不能一起使用时一起使用(!)但是,我将此脚本仅用于测试,所以我并不太担心。(我认为,“真正地”修复使用消息需要时间比我可以为这项任务腾出的时间多得多,但如果你知道一个聪明的技巧,请发表评论。)

于 2020-03-31T19:08:57.817 回答
0

子解析器?

类似于unhammer的答案,但具有更多的用户控制权。注意:我还没有实际测试过这个方法,但是理论上它应该可以工作并且具有 python 的功能。

您可以创建两个解析器,一个用于两个组中的每一个,并使用条件来执行互斥部分。本质上仅将 argparse 用于部分参数解析。使用这种方法,您也可以超越unhammer答案的限制。

# Python 3
import argparse

try:
    parser = argparse.ArgumentParser()
    parser.add_argument('-a')
    parser.add_argument('-b')
    args = parser.parse_args
except argparse.ArgumentError:
    parser = argparse.ArgumentParser()
    parser.add_argument('-c')
    parser.add_argument('-d')
    args = parser.parse_args
于 2018-07-20T02:30:00.227 回答