Python 具有argparse和多个-v选项的详细级别

Python 具有argparse和多个-v选项的详细级别,python,argparse,Python,Argparse,我希望能够通过向命令行添加更多的-v选项来指定不同的详细级别。例如: $ myprogram.py $ myprogram.py -v $ myprogram.py -vv $ myprogram.py -v -v -v 将分别导致verbose=0、verbose=1、verbose=2和verbose=3。如何使用argparse实现这一点 或者,也可以像这样指定它 $ myprogram -v 2 argparse支持append操作,该操作允许您指定多个参数。选中,搜索“ap

我希望能够通过向命令行添加更多的-v选项来指定不同的详细级别。例如:

$ myprogram.py    
$ myprogram.py -v
$ myprogram.py -vv
$ myprogram.py -v -v -v
将分别导致verbose=0、verbose=1、verbose=2和verbose=3。如何使用argparse实现这一点

或者,也可以像这样指定它

$ myprogram -v 2

argparse
支持
append
操作,该操作允许您指定多个参数。选中,搜索“
append

您可以使用
nargs=”?“
(在
-v
标志后接受0或1个参数)和自定义操作(处理0或1个参数):

从命令行运行
script.py-v-v
会产生

['-v', '-v'] --> Namespace(verbose=2)
--------------------------------------------------------------------------------
-v         --> Namespace(verbose=1)
-v -v      --> Namespace(verbose=2)
-v -v -v   --> Namespace(verbose=3)
-vv        --> Namespace(verbose=2)
-vvv       --> Namespace(verbose=3)
-v 2       --> Namespace(verbose=2)

取消对print语句的注释,以便更好地查看
VAction
正在执行的操作。

您可以使用
append\u const
处理问题的第一部分。否则,您可能会陷入编写自定义操作的困境,正如罚款中所建议的那样

输出:

Namespace(v=0)
Namespace(v=1)
Namespace(v=2)
Namespace(v=2)
Namespace(v=3)
Namespace(verbose=0)
Namespace(verbose=1)
Namespace(verbose=2)
Namespace(verbose=2)
Namespace(verbose=3)
Namespace(verbose=7)

您第一次提出的方法很可能会混淆。不同详细程度的不同选项名称,或者一个详细程度标志(可选后跟详细程度的数字指示器)不太可能混淆用户,并允许更灵活地分配详细程度。

argparse支持:

输出:

Namespace(v=0)
Namespace(v=1)
Namespace(v=2)
Namespace(v=2)
Namespace(v=3)
Namespace(verbose=0)
Namespace(verbose=1)
Namespace(verbose=2)
Namespace(verbose=2)
Namespace(verbose=3)
Namespace(verbose=7)

唯一一个非常小的麻烦是,如果不希望任何
-v
参数为您提供详细级别为0的详细级别,而不是
None
扩展unutbu的答案,则必须显式设置
default=0
,下面是一个自定义操作,包括处理--quiet/-q组合。这是在Python3中测试的。在Python>=2.7中使用它应该没什么大不了的

class ActionVerbose(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
        #print(parser, args, values, option_string)
        # Obtain previously set value in case this option call is incr/decr only
        if args.verbose == None:
            base = 0
        else:
            base = args.verbose
        # One incr/decr is determined in name of option in use (--quiet/-q/-v/--verbose)
        option_string = option_string.lstrip('-')
        if option_string[0] == 'q':
            incr = -1
        elif option_string[0] == 'v':
            incr = 1
        else:
            raise argparse.ArgumentError(self,
                                         'Option string for verbosity must start with v(erbose) or q(uiet)')
        # Determine if option only or values provided
        if values==None:
            values = base + incr
        else:
            # Values might be an absolute integer verbosity level or more 'q'/'v' combinations
            try:
                values = int(values)
            except ValueError:
                values = values.lower()
                if not re.match('^[vq]+$', values):
                    raise argparse.ArgumentError(self,
                                                 "Option string for -v/-q must contain only further 'v'/'q' letters")
                values = base + incr + values.count('v') - values.count('q')
        setattr(args, self.dest, values)
    @classmethod
    def add_to_parser(cls,
                      parser, dest='verbose', default=0,
                      help_detail='(0:errors, 1:info, 2:debug)'):
        parser.add_argument('--verbose', nargs='?', action=ActionVerbose, dest=dest, metavar='level',
                            default=default,
                            help='Increase or set level of verbosity {}'.format(help_detail))
        parser.add_argument('-v',        nargs='?', action=ActionVerbose, dest=dest, metavar='level',
                            help='Increase or set level of verbosity')
        parser.add_argument('--quiet',   nargs='?', action=ActionVerbose, dest=dest, metavar='level',
                            help='Decrease or set level of verbosity')
        parser.add_argument('-q',        nargs='?', action=ActionVerbose, dest=dest, metavar='level',
                            help='Decrease or set level of verbosity')
有一个方便的类方法可用于为
--verbose
-v
-q
--quiet
设置所有四个选项处理程序。像这样使用它:

parser = argparse.ArgumentParser()
ActionVerbose.add_to_parser(parser, default=defaults['verbose'])
# add more arguments here with: parser.add_argument(...)
args = parser.parse_args()
使用具有这些参数的脚本时,可以执行以下操作:

./script -vvvvvv -v 4 -v 0 -v -vvv --verbose --quiet 2 -v qqvvqvv
使用此命令行
args.verbose
将是
4

  • 任何带有给定数字的
    -v/-q/-verbose/-quiet
    都是该给定数字(=详细程度)的绝对
    参数集
  • 任何不带数字的
    -v/--verbose
    都是该级别的增量
  • 任何没有数字的
    -q/--quiet
    都是该级别的减量
  • 任何
    -v/-q
    之后都可能立即出现更多
    v/q
    字母,产生的级别是
    旧级别+sum(count('v'))-sum(count('q'))
  • 总体默认值为0
如果您想要不同的行为,自定义操作应该很容易修改。例如,有些人喜欢任何
--quiet
将级别重置为0,甚至是-1。为此,请从
-q
-quiet
的add_参数中删除
nargs
,如果选项字符串[0]='q'
,则也可以将
硬编码设置为value=0

如果使用错误,正确的解析器错误会很好地打印出来:

./script -vvvvvv -v 4 -v 0 -v -vvv --verbose --quiet 2 -v qqvvqvav
usage: script [-h] [--verbose [level]]
              [-v [level]] [--quiet [level]] [-q [level]]
script: error: argument -v: Option string for -v/-q must contain only further 'v'/'q' letters

下面是我对此的看法,它不使用任何新类,在Python 2和3中都可以使用,并且支持使用“-v”/--verbose”和“-q”/--quiet对默认值进行相对调整,但它不支持使用数字,例如“-v2”:

例如:

$ python2 verbosity.py -vvv
DEBUG
$ python3 verbosity.py -vvv -q
INFO
$ python2 verbosity.py -qqq -vvv -q
WARNING
$ python2 verbosity.py -qqq
CRITICAL

我想出了一个替代方案;虽然它并不完全符合OP的要求,但它满足了我的要求,我认为值得分享

使用互斥组计算短选项数或存储长选项的整数值

import argparse

parser = argparse.ArgumentParser()
verbosity_group = parser.add_mutually_exclusive_group()
verbosity_group.add_argument(
  '-v',
  action='count',
  dest='verbosity',
  help='Turn on verbose output. Use more to turn up the verbosity level'
)
verbosity_group.add_argument(
  '--verbose',
  action='store',
  type=int,
  metavar='N',
  dest='verbosity',
  help='Set verbosity level to `N`'
)
parser.set_defaults(
  verbosity=0
)
parser.parse_args()

如您所见,它允许您“堆叠”单个字符选项,并允许您使用长选项名显式设置值。缺点是不能将long选项用作开关(最后一个示例生成一个异常)。

我喜欢这个解决方案;我认为使用
append\u const
所提供的代码的简单性值得放弃
-v2
。选择我会接受的答案不是一个容易的选择。我喜欢你简单的回答。使用
append\u const
还可以添加
-q
参数。使用
dest='v',const=-1
,它将撤消任何
-v
。我将其与
default=[2]
一起使用,这样我就可以将结果映射到日志模块级别,从WARN开始,让您在刻度上下移动-q/v。如果您设置
allow_abbrev=False
,则
-vv
输入不起作用。但是您仍然可以执行
-v-v
,这种方法并不少见。例如,SSH使用它,这不允许使用
myprogram-v2
语法。同时,
action='count'
也出现在文档中。这似乎是对OP问题的最好、最简单的回答。不满足ops的可选要求,但对我来说非常简单和好。谢谢不需要(不再)自定义操作。看看Ben的答案很有教育意义但是。。。未指定时,详细信息为
None
(可能需要
0
)。当你有其他的选择(不仅仅是一个)并且你想要有自由的参数顺序时,它也不能很好地发挥作用。不幸的是,它甚至不起作用。当您在实际的命令行上多次传递
-v
时,您不会得到
['-v-v']
,而是得到
['-v'、'-v'、'-v']
,因此您的代码给出了
verbose=1
@TomaszGandor:感谢您的错误报告。多个<代码> -v>代码> s的问题已经被纠正了。你应该考虑标记为正确答案,因为人们通过谷歌搜索找到这一点,而 -V 2 特性和自定义代码片段对于大多数用户来说都是最重要的。谢谢你的评论,指出了正确的答案。我真的很喜欢这个实现。它非常干净,但也有一些缺陷。您的日志级别与日志库中的日志级别不同,因此仍需要转换它们。
$ python2 verbosity.py -vvv
DEBUG
$ python3 verbosity.py -vvv -q
INFO
$ python2 verbosity.py -qqq -vvv -q
WARNING
$ python2 verbosity.py -qqq
CRITICAL
import argparse

parser = argparse.ArgumentParser()
verbosity_group = parser.add_mutually_exclusive_group()
verbosity_group.add_argument(
  '-v',
  action='count',
  dest='verbosity',
  help='Turn on verbose output. Use more to turn up the verbosity level'
)
verbosity_group.add_argument(
  '--verbose',
  action='store',
  type=int,
  metavar='N',
  dest='verbosity',
  help='Set verbosity level to `N`'
)
parser.set_defaults(
  verbosity=0
)
parser.parse_args()
parser.parse_args([])
# Namespace(verbosity=0)
parser.parse_args(['-v', '-vv'])
# Namespace(verbosity=3)
parser.parse_args(['--verbose=4'])
# Namespace(verbosity=4)
parser.parse_args(['--verbose'])
# error: argument --verbose: expected one argument