2011-12-15 42 views
17

我有一个脚本,我向用户询问要执行的预定义操作的列表。我还希望能够在用户没有定义任何内容时采取特定的操作列表。然而,似乎试图将这两者结合在一起是不可能的。python argparse - 带选项的可选append参数

当用户没有给出参数,他们收到一个错误,默认的选择是无效

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']]) 
args = p.parse_args([]) 
>>> usage: [-h] [{clear,copy,dump,lock} [{clear,copy,dump,lock} ...]] 
: error: argument action: invalid choice: [['dump', 'clear']] (choose from 'clear', 'copy', 'dump', 'lock') 

当他们这样做定义一组动作,得到的命名空间拥有了用户的操作追加到默认,而不是替换默认

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']]) 
args = p.parse_args(['lock']) 
args 
>>> Namespace(action=[['dump', 'clear'], ['dump']]) 
+0

我不确定这是否已被类似的bug解决了报告:http://bugs.python.org/issue9625。处理这种情况的一种可能方法是使用自定义操作,而不是使用`choices`关键字。看看[这个问题]上接受的答案(http://stackoverflow.com/questions/4194948/python-argparse-is-there-a-way-to-specify-a-range-in-nargs) – Chris 2011-12-15 22:04:32

回答

15

你需要什么可以使用来进行定制argparse.Action如下面的例子:

import argparse 

parser = argparse.ArgumentParser() 

class DefaultListAction(argparse.Action): 
    CHOICES = ['clear','copy','dump','lock'] 
    def __call__(self, parser, namespace, values, option_string=None): 
     if values: 
      for value in values: 
       if value not in self.CHOICES: 
        message = ("invalid choice: {0!r} (choose from {1})" 
           .format(value, 
             ', '.join([repr(action) 
                for action in self.CHOICES]))) 

        raise argparse.ArgumentError(self, message) 
      setattr(namespace, self.dest, values) 

parser.add_argument('actions', nargs='*', action=DefaultListAction, 
        default = ['dump', 'clear'], 
        metavar='ACTION') 

print parser.parse_args([]) 
print parser.parse_args(['lock']) 

脚本的输出是:

$ python test.py 
Namespace(actions=['dump', 'clear']) 
Namespace(actions=['lock']) 
1

的行动被追加,因为“行动=‘追加’”你传递的参数来argparse。

删除此参数后,用户传递的参数将自行显示,但当没有参数传递时,程序会引发错误。

为第一个参数添加一个' - '前缀以最懒散的方式解决了这个问题。

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('--action', nargs='*', choices=acts, default=[['dump', 'clear']]) 
args = p.parse_args() 

对这个缺点的方法是,用户通过该选项必须现在“--action”前面,如:

app.py --action clear dump copy 
3

您可以测试用户是否提供行动(在这种情况下,解析它作为一个需要,位置参数),或不供给动作(在这种情况下,解析它与默认可选参数):

import argparse 
import sys 

acts = ['clear', 'copy', 'dump', 'lock'] 
p = argparse.ArgumentParser() 
if sys.argv[1:]: 
    p.add_argument('action', nargs = '*', choices = acts) 
else: 
    p.add_argument('--action', default = ['dump', 'clear']) 

args = p.parse_args() 
print(args) 

运行时,产生以下结果:

% test.py 
Namespace(action=['dump', 'clear']) 
% test.py lock 
Namespace(action=['lock']) 
% test.py lock dump 
Namespace(action=['lock', 'dump']) 

你可能有其他选项解析为好。在这种情况下,你可以使用parse_known_args来解析其他选项,然后处理unknown参数在第二遍:

import argparse 

acts = ['clear', 'copy', 'dump', 'lock'] 
p = argparse.ArgumentParser() 
p.add_argument('--foo') 
args, unknown = p.parse_known_args() 
if unknown: 
    p.add_argument('action', nargs = '*', choices = acts) 
else: 
    p.add_argument('--action', default = ['dump', 'clear']) 

p.parse_args(unknown, namespace = args) 
print(args) 

运行时,产生这些结果:

% test.py 
Namespace(action=['dump', 'clear'], foo=None) 
% test.py --foo bar 
Namespace(action=['dump', 'clear'], foo='bar') 
% test.py lock dump 
Namespace(action=['lock', 'dump'], foo=None) 
% test.py lock dump --foo bar 
Namespace(action=['lock', 'dump'], foo='bar') 
4

在文档(http://docs.python.org/dev/library/argparse.html#default),这是说:

对于NARGS等于位置参数?或*,当没有命令行参数时,使用默认值。

然后,如果我们这样做:

acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', choices=acts, default='clear')  
print p.parse_args([]) 

我们得到了我们所期望的

Namespace(action='clear') 

问题是,当你把一个表作为默认值。 但我在doc看到它,

parser.add_argument('bar', nargs='*', default=[1, 2, 3], help='BAR!') 

所以,我不知道:-(

总之,这里是一个解决办法,你想要做的工作:

import sys, argparse 
acts = ['clear','copy','dump','lock'] 
p = argparse.ArgumentParser() 
p.add_argument('action', nargs='*', choices=acts) 
args = ['dump', 'clear'] # I set the default here ... 
if sys.argv[1:]: 
    args = p.parse_args() 
print args 
1

最后我做了以下内容:

  • 没有append
  • 加空单可能的选择,否则空输入打破
  • 没有默认
  • 检查空单后,并设置实际的默认在这种情况下

例子:

parser = argparse.ArgumentParser() 
parser.add_argument(
    'is', 
    type=int, 
    choices=[[], 1, 2, 3], 
    nargs='*', 
) 

args = parser.parse_args(['1', '3']) 
assert args.a == [1, 3] 

args = parser.parse_args([]) 
assert args.a == [] 
if args.a == []: 
    args.a = [1, 2] 

args = parser.parse_args(['1', '4']) 
# Error: '4' is not valid. 
相关问题