python - 互斥参数抱怨操作索引

python - mutually exclusive arguments complains about action index

我正在尝试对参数进行分组,以便用户可以执行以下任一操作:

python sample.py scan -a 1 -b 2
or
python sample.pt save -d /tmp -n something

这是我的代码:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser(
            description='this is the description'
            )
    parser.add_argument('op', choices=['scan','save'], help='operation', default='scan')
    root_group = parser.add_mutually_exclusive_group()

    group1 = root_group.add_argument_group('g1', 'scan')
    group1.add_argument('-a', help='dir1')
    group1.add_argument('-b', help='dir2')

    group2 = root_group.add_argument_group('g2', 'save')
    group2.add_argument('-d', help='dir')
    group2.add_argument('-n', help='name')

    args = parser.parse_args()
    print args

我运行 python sample.py --help

我遇到了一个错误。有人可以告诉我如何解决吗?

Traceback (most recent call last):
  File "sample.py", line 18, in <module>
    args = parser.parse_args()
  File "C:\Python27\lib\argparse.py", line 1688, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "C:\Python27\lib\argparse.py", line 1720, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "C:\Python27\lib\argparse.py", line 1926, in _parse_known_args
    start_index = consume_optional(start_index)
  File "C:\Python27\lib\argparse.py", line 1866, in consume_optional
    take_action(action, args, option_string)
  File "C:\Python27\lib\argparse.py", line 1794, in take_action
    action(self, namespace, argument_values, option_string)
  File "C:\Python27\lib\argparse.py", line 994, in __call__
    parser.print_help()
  File "C:\Python27\lib\argparse.py", line 2313, in print_help
    self._print_message(self.format_help(), file)
  File "C:\Python27\lib\argparse.py", line 2287, in format_help
    return formatter.format_help()
  File "C:\Python27\lib\argparse.py", line 279, in format_help
    help = self._root_section.format_help()
  File "C:\Python27\lib\argparse.py", line 209, in format_help
    func(*args)
  File "C:\Python27\lib\argparse.py", line 317, in _format_usage
    action_usage = format(optionals + positionals, groups)
  File "C:\Python27\lib\argparse.py", line 388, in _format_actions_usage
    start = actions.index(group._group_actions[0])
IndexError: list index out of range

如果我添加 action='store_const',错误就会消失,并且会出现一个新的错误,要求输入 4 个输入。

Argparse似乎不​​完全支持将一个组添加到另一个组中。发生此错误是因为 Argparse 需要 root_group 进行某种操作。解决方法是向组中添加一个参数:

import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
            description='this is the description'
            )

    # This is now redundant. We can remove it
    # parser.add_argument('op', choices=['scan','save'], help='operation', default='scan')
    root_group = parser.add_mutually_exclusive_group()

    # Workaround
    root_group.add_argument('--scan', help='scan', action='store_true')
    root_group.add_argument('--save', help='save', action='store_true')

    group1 = root_group.add_argument_group('g1', 'scan')
    group2 = root_group.add_argument_group('g2', 'save')

    group1.add_argument('-a', help='dir1')
    group1.add_argument('-b', help='dir2')

    group2.add_argument('-d', help='dir', default='')
    group2.add_argument('-n', help='name')

    args = parser.parse_args()
    print args 

请注意,我们使用的是 --scan--save。要避免使用 -- 前缀,您可能需要 Sub-commands 的帮助。详情可见here.

多亏了@skyline 上面的 link,我让它与子解析器一起工作:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser(
            description='this is the description'
            )

    scan_parser = argparse.ArgumentParser(add_help=False)
    scan_parser.add_argument('-a', '--a', help='first num', required=True)
    scan_parser.add_argument('-b', '--b', help='second num', required=True)

    save_parser = argparse.ArgumentParser(add_help=False)
    save_parser.add_argument('-d', '--d', help='directory path', required=True)
    save_parser.add_argument('-n', '--n', help='name of the file', required=True)

    sp = parser.add_subparsers()

    sp_scan = sp.add_parser('scan', parents=[scan_parser], help='scans directories')
    sp_save = sp.add_parser('save', parents=[save_parser], help='saves something')
    args = parser.parse_args()
    print args

格式化 usage 行时发生错误,这是 root_group 没有任何 _group_actions 的结果。我从其他错误问题中知道使用格式化程序很脆弱。

argument_groupsmutually_exclusive_groups 不是为了嵌套而设计的。尽管名称相似(和 class 传统),但它们的用途却截然不同。 argument_groups 控制 help 行的显示。 mutually_exclusive_groups 控制用法显示,并引发错误。

在你的例子中,添加到 group1group2 的参数添加到主解析器列表,但没有添加到 root_group 用于排他性检查的列表(或使用格式)。

如果我直接向 root_group 添加参数,help 有效,并生成:

In [19]: parser.print_help()
usage: ipython3 [-h] [-a A] [-b B] [-d D] [-n N] [--foo FOO] {scan,save}

this is the description

positional arguments:
  {scan,save}  operation

optional arguments:
  -h, --help   show this help message and exit
  --foo FOO

您可以从 'related' 侧边栏中看到,许多人询问过如何向 mutually_exclusive_groups 添加参数组。允许具有各种逻辑条件的嵌套组的补丁在未来还有很长的路要走。

但是,正如您发现的那样,子解析器机制可以很好地处理您的特定情况。

你不需要使用parents机制:

sp = parser.add_subparsers()

sp_scan = sp.add_parser('scan', help='scans directories')
sp_scan.add_argument('-a', '--a', help='first num', required=True)
sp_scan.add_argument('-b', '--b', help='second num', required=True)

sp_save = sp.add_parser('save', parents=[save_parser], help='saves something')
sp_save.add_argument('-d', '--d', help='directory path', required=True)
sp_save.add_argument('-n', '--n', help='name of the file', required=True)

parents 机制在这里起作用,但更适用于解析器在别处定义(并导入)或在多个子解析器中重用的情况。例如,如果您有许多共享一组核心参数(以及它们自己独特的参数)的子解析器。