47

我希望能够通过在命令行中添加更多 -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
4

8 回答 8

151

argparse 支持action='count'

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='count', default=0)

for c in ['', '-v', '-v -v', '-vv', '-vv -v', '-v -v --verbose -vvvv']:
    print(parser.parse_args(c.split()))

输出:

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

唯一非常小的问题是,default=0如果您不希望任何-v参数为您提供详细级别 0 而不是None.

于 2011-12-01T02:35:34.100 回答
25

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

import sys
import argparse

class VAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, const=None, 
                 default=None, type=None, choices=None, required=False, 
                 help=None, metavar=None):
        super(VAction, self).__init__(option_strings, dest, nargs, const, 
                                      default, type, choices, required, 
                                      help, metavar)
        self.values = 0
    def __call__(self, parser, args, values, option_string=None):
        # print('values: {v!r}'.format(v=values))
        if values is None:
            self.values += 1
        else:
            try:
                self.values = int(values)
            except ValueError:
                self.values = values.count('v')+1
        setattr(args, self.dest, self.values)

# test from the command line
parser = argparse.ArgumentParser()
parser.add_argument('-v', nargs='?', action=VAction, dest='verbose')
args = parser.parse_args()
print('{} --> {}'.format(sys.argv[1:], args))

print('-'*80)

for test in ['-v', '-v -v', '-v -v -v', '-vv', '-vvv', '-v 2']:
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', nargs='?', action=VAction, dest='verbose')
    args=parser.parse_args([test])
    print('{:10} --> {}'.format(test, args))

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做什么。

于 2011-05-20T19:33:38.453 回答
15

您可以使用 处理问题的第一部分append_const。否则,您可能无法编写自定义操作,正如unutbu 的最佳答案中所建议的那样。

import argparse

ap = argparse.ArgumentParser()
ap.add_argument('-v', action = 'append_const', const = 1)

for c in ['', '-v', '-v -v', '-vv', '-vv -v']:
    opt = ap.parse_args(c.split())
    opt.v = 0 if opt.v is None else sum(opt.v)
    print opt

输出:

Namespace(v=0)
Namespace(v=1)
Namespace(v=2)
Namespace(v=2)
Namespace(v=3)
于 2011-05-20T19:44:26.913 回答
8

这是我对此的看法,它不使用任何新类,适用于 Python 2 和 3,并支持使用“-v”/“--verbose”和“-q”/“--quiet”从默认值进行相对调整,但它支持使用数字,例如“-v 2”:

#!/usr/bin/env python
import argparse
import logging
import sys

LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
DEFAULT_LOG_LEVEL = "INFO"


def main(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--verbose", "-v",
        dest="log_level",
        action="append_const",
        const=-1,
    )
    parser.add_argument(
        "--quiet", "-q",
        dest="log_level",
        action="append_const",
        const=1,
    )

    args = parser.parse_args(argv[1:])
    log_level = LOG_LEVELS.index(DEFAULT_LOG_LEVEL)

    # For each "-q" and "-v" flag, adjust the logging verbosity accordingly
    # making sure to clamp off the value from 0 to 4, inclusive of both
    for adjustment in args.log_level or ():
        log_level = min(len(LOG_LEVELS) - 1, max(log_level + adjustment, 0))

    log_level_name = LOG_LEVELS[log_level]
    print(log_level_name)
    logging.getLogger().setLevel(log_level_name)


if __name__ == "__main__":
    main(sys.argv)

例子:

$ python2 verbosity.py -vvv
DEBUG
$ python3 verbosity.py -vvv -q
INFO
$ python2 verbosity.py -qqq -vvv -q
WARNING
$ python2 verbosity.py -qqq
CRITICAL
于 2018-11-14T04:04:52.020 回答
4

扩展 unutbu 的答案,这是一个自定义操作,包括处理 --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.verbose4.

  • 任何-v/-q/--verbose/--quiet具有给定数字的都是该给定数字的硬绝对集args.verbose(=详细程度)。
  • 任何-v/--verbose没有数字的都是该级别的增量。
  • 任何-q/--quiet没有数字的都是该级别的递减。
  • Any-v/-q可能会立即跟进更多v/q字母,结果级别为the old level + sum(count('v')) - sum(count('q'))
  • 整体默认为 0

如果您想要不同的行为,自定义操作应该很容易修改。例如,有些人更喜欢将任何--quiet级别重置为 0,甚至重置为 -1。为此,nargs从 and 的 add_argument 中删除,-q并将--quiet硬编码为 set value = 0 if option_string[0] == 'q'

如果使用错误,可以很好地打印正确的解析器错误:

./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
于 2012-08-28T09:03:47.380 回答
2

argparse支持append让您指定多个参数的操作。检查http://docs.python.org/library/argparse.html,搜索“ append”。

于 2011-05-20T19:29:26.353 回答
0

您提出的第一个方法更容易混淆。不同详细级别的不同选项名称,或者一个详细标志可选地后跟详细级别的数字指示符,不太可能使用户感到困惑,并且在分配详细级别时允许更大的灵活性。

于 2011-05-21T02:40:47.143 回答
0

我想出了一个替代方案;虽然它不完全符合 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()
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

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

于 2020-05-08T11:18:34.573 回答