【问题标题】:How to define a mutually exclusive group of two positional arguments?如何定义两个位置参数的互斥组?
【发布时间】:2013-03-09 22:26:45
【问题描述】:

我想用argparse来做一些代码,可以通过以下两种方式使用:

./tester.py all
./tester.py name someprocess

即要么指定all,要么指定name 和一些额外的字符串。

我尝试如下实现:

import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('all', action='store_true', \
        help = "Stops all processes")
group.add_argument('name', \
        help = "Stops the named process")

print parser.parse_args()

这给了我一个错误

ValueError: mutually exclusive arguments must be optional

知道怎么做吗?在这种情况下,我也想避免使用子解析器。

【问题讨论】:

  • 为什么要避免使用 sup 解析器?这看起来完全像一个子解析器问题!
  • 它们已经在 suparsers 上运行。我想保持浅...但是如果没有其他解决方案,我将尝试使用两个级别的子解析器。
  • all 更改为--allname 更改为--name
  • @hughdbrown:我知道这行得通,但不是我问的。

标签: python python-2.7 argparse


【解决方案1】:

这个问题已经有一年了,但由于所有答案都暗示了不同的语法,我会给出更接近 OP 的内容。

首先,OP代码的问题:

定位store_true 没有意义(即使它是允许的)。它不需要任何参数,所以它总是True。给出“全部”将产生error: unrecognized arguments: all

另一个参数采用一个值并将其分配给name 属性。它不接受额外的process 值。

关于mutually_exclusive_group。甚至在parse_args 之前就会出现该错误消息。为了使这样一个组有意义,所有的选择都必须是可选的。这意味着要么有一个-- 标志,要么是一个nargs 等于?* 的位置。并且在组中拥有多个这样的位置是没有意义的。

使用--all--name 的最简单替代方法是:

p=argparse.ArgumentParser()
p.add_argument('mode', choices=['all','name'])
p.add_argument('process',nargs='?')

def foo(args):
    if args.mode == 'all' and args.process:
        pass # can ignore the  process value or raise a error
    if args.mode == 'name' and args.process is None:
        p.error('name mode requires a process')

args = p.parse_args()
foo(args) # now test the namespace for correct `process` argument.

接受的命名空间如下所示:

Namespace(mode='name', process='process1')
Namespace(mode='all', process=None)

choices 模仿子解析器参数的行为。在parse_args 之后进行自己的测试通常比让argparse 做一些特别的事情更简单。

【讨论】:

  • +1 确实最简单,我忘了choices,我没仔细看OP在name参数后面只需要一个进程名。
  • 这还不够好。默认帮助没有表明name 需要process,而all 则忽略/不允许。八年后,没有人愿意在 argparse 中实现这样的功能。我不明白为什么互斥组不能包含位置参数,只要整个参数解析器中只有一个位置参数(如 OP 的情况和我的情况)。
  • @БоратСагдиев,我指出可以在这样的组中使用具有正确nargs(和默认值)的位置。 OP有错误的位置。为一组奇特的条件格式化使用行并不是一项简单的任务。自己写,或使用description/epilog 来说明您的要求。
  • 确实,我用nargs='?' 的一个选项和一个位置参数尝试了它,它似乎在大多数情况下都有效。当我首先将选项添加到组中,然后添加位置参数时,帮助正确地表明这两个参数是互斥的。但是,当先添加位置然后添加选项时,它只会显示[-o] [positional],就好像它们可以结合使用一样,尽管实际上结合使用它们会导致错误,正如预期的那样。
  • @БоратСагдиев,我不记得为什么的细节,但是是的,为了正确的使用显示,位置应该是组的最后一个。使用格式化程序不是很复杂,很容易被破坏。但是在解析组期间,测试是由不相关的代码执行的。几年前,当我探索实现更通用的嵌套组机制时,使用格式化是一项比输入(定义组)或测试要复杂得多的任务。
【解决方案2】:
import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-a','--all', action='store_true', \
        help = "Stops all processes")
group.add_argument('-n','--name', \
        help = "Stops the named process")

print parser.parse_args()

./tester.py -h

usage: zx.py [-h] (-a | -n NAME)

optional arguments:
  -h, --help            show this help message and exit
  -a, --all             Stops all processes
  -n NAME, --name NAME  Stops the named process

【讨论】:

    【解决方案3】:

    "OR 名字加上一些额外的字符串。"

    位置参数不能带额外的字符串

    我认为最适合您的解决方案是(命名为 test.py):

    import argparse
    p = argparse.ArgumentParser()
    meg = p.add_mutually_exclusive_group()
    meg.add_argument('-a', '--all', action='store_true', default=None)
    meg.add_argument('-n', '--name', nargs='+')
    print p.parse_args([])
    print p.parse_args(['-a'])
    print p.parse_args('--name process'.split())
    print p.parse_args('--name process1 process2'.split())
    print p.parse_args('--all --name process1'.split())
    

    $ python test.py

    Namespace(all=None, name=None)
    Namespace(all=True, name=None)
    Namespace(all=None, name=['process'])
    Namespace(all=None, name=['process1', 'process2'])
    usage: t2.py [-h] [-a | -n NAME [NAME ...]]
    t2.py: error: argument -n/--name: not allowed with argument -a/--all
    

    【讨论】:

      【解决方案4】:

      我同意这看起来完全像一个子解析器问题,如果您不想通过使用 --all--name 使其成为可选参数,我的一个建议就是忽略allname 一起使用,并使用以下语义:

      1. 如果在没有任何参数的情况下调用 tester.py,则停止所有进程。
      2. 如果使用某些参数调用 tester.py,则仅停止这些进程。

      这可以使用:

      import argparse, sys
      parser = argparse.ArgumentParser()
      parser.add_argument('processes', nargs='*')
      parsed = parser.parse(sys.argv[1:])
      print parsed
      

      其行为如下:

      $ python tester.py 命名空间(进程=[]) $ python tester.py proc1 命名空间(进程=['proc1'])

      或者,如果您坚持自己的语法,您可以创建一个自定义类。实际上,您并没有“互斥组”的情况,因为我假设如果指定了all,您将忽略其余的参数(即使name 是其他参数之一),并且当@指定了 987654330@,之后的任何内容都将被视为进程的名称。

      import argparse
      import sys
      class AllOrName(argparse.Action):
          def __call__(self, parser, namespace, values, option_string=None):
              if len(values)==0:
                  raise argparse.ArgumentError(self, 'too few arguments')
              if values[0]=='all':
                  setattr(namespace, 'all', True)
              elif values[0]=='name':
                  if len(values)==1:
                      raise argparse.ArgumentError(self, 'please specify at least one process name')
                  setattr(namespace, 'name', values[1:])
              else:
                  raise argparse.ArgumentError(self, 'only "all" or "name" should be specified')
      
      parser = argparse.ArgumentParser()
      parser.add_argument('processes', nargs='*', action=AllOrName)
      parsed = parser.parse_args(sys.argv[1:])
      print parsed
      

      具有以下行为:

      $ python argparse_test.py 名称 用法:argparse_test.py [-h] [进程[进程...]] argparse_test.py:错误:参数进程:请指定至少一个进程名称 $ python argparse_test.py 名称 proc1 命名空间(名称=['proc1'],进程=无) $ python argparse_test.py 全部 命名空间(全部=真,进程=无) $ python argparse_test.py 主机 用法:argparse_test.py [-h] [进程[进程...]] argparse_test.py:错误:参数过程:只应指定“全部”或“名称” $ python argparse_test.py 用法:argparse_test.py [-h] [进程[进程...]] argparse_test.py:错误:参数过程:参数太少

      【讨论】:

        【解决方案5】:

        这可能就是你要找的:

        group.add_argument('--all', dest=is_all, action='store_true')
        group.add_argument('--name', dest=names, nargs='+')
        

        传递 --name 然后将需要列表一个值并将它们存储为列表。

        【讨论】:

          猜你喜欢
          • 2017-01-03
          • 2016-05-04
          • 1970-01-01
          • 2013-03-29
          • 1970-01-01
          • 2015-10-19
          • 2019-09-13
          • 2013-09-22
          • 2014-02-03
          相关资源
          最近更新 更多