如何定义两个位置参数的互斥组?

时间:2013-03-20 17:05:41

标签: python python-2.7 argparse

我想使用argparse来制作一些代码,用于以下两种方式:

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

即。指定allname并附加一些字符串。

我试图按如下方式实施:

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

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

5 个答案:

答案 0 :(得分:11)

这个问题已经有一年了,但由于所有的答案都提出了不同的语法,我会给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模仿subparsers参数的行为。在parse_args之后进行自己的测试通常比使argparse做一些特别的事情更简单。

答案 1 :(得分:0)

  

"或者用一些额外的字符串命名。"

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

我认为最适合您的解决方案是(名为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

答案 2 :(得分:0)

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

  1. 如果在没有任何参数的情况下调用tester.py,请停止所有进程。
  2. 如果使用某些参数调用tester.py,请仅停止这些进程。
  3. 可以使用以下方法完成:

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

    表现如下:

    $ python tester.py
    Namespace(processes=[])
    $ python tester.py proc1
    Namespace(processes=['proc1'])
    

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

    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 name
    usage: argparse_test.py [-h] [processes [processes ...]]
    argparse_test.py: error: argument processes: please specify at least one process name
    
    $ python argparse_test.py name proc1
    Namespace(name=['proc1'], processes=None)
    
    $ python argparse_test.py all
    Namespace(all=True, processes=None)
    
    $ python argparse_test.py host
    usage: argparse_test.py [-h] [processes [processes ...]]
    argparse_test.py: error: argument processes: only "all" or "name" should be specified
    
    $ python argparse_test.py
    usage: argparse_test.py [-h] [processes [processes ...]]
    argparse_test.py: error: argument processes: too few arguments
    

答案 3 :(得分:0)

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

答案 4 :(得分:-2)

这可能就是你要找的东西:

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

传递--name将在列表中要求一个值并将它们存储为列表。