如何在脚本中实现--verbose或-v选项?


94

我知道--verbose-v来自几种工具,我想在自己的一些脚本和工具中实现这一点。

我想到放置:

if verbose:
    print ...

通过我的源代码,以便如果用户通过该-v选项,则变量verbose将设置为,True并且将打印文本。

这是正确的方法还是有更常见的方法?

另外:我并没有要求实现参数解析的方法。我知道怎么做。我只对详细选项特别感兴趣。


9
为什么不使用日志记录模块并默认设置日志级别INFO以及通过--verbose设置DEBUG?最好不要重新实现该语言中已有的任何内容……
蒂姆(Tim)

3
@Tim,我同意,但是日志记录模块非常痛苦。
mlissner

Answers:


106

我的建议是使用一个函数。但是不要像if可能会尝试将in 放入函数那样,而是这样做:

if verbose:
    def verboseprint(*args):
        # Print each argument separately so caller doesn't need to
        # stuff everything to be printed into a single string
        for arg in args:
           print arg,
        print
else:   
    verboseprint = lambda *a: None      # do-nothing function

(是的,您可以在if语句中定义一个函数,只有在条件为true时才会被定义!)

如果您使用的是Python 3,则其中print已经是一个函数(或者如果您愿意使用print,在2.x中用作函数from __future__ import print_function),它甚至更简单:

verboseprint = print if verbose else lambda *a, **k: None

这样,如果关闭了详细模式(使用lambda),则该函数被定义为“不执行任何操作”,而不是不断测试该verbose标志。

如果用户可以程序运行期间更改详细模式,则这是错误的方法(您需要if在函数中使用),但是由于您是使用命令行标志设置的,因此您只需要一次做决定。

然后,您可以verboseprint("look at all my verbosity!", object(), 3)在每次要打印“详细”消息时使用例如。


1
更好的是,将其用作print函数:接受许多参数。它可以print(*args)在3.x和for arg in args: print arg,2.x中实现。主要优点是它允许在一条消息中混合字符串和其他类型的内容,而无需显式str调用/格式化和串联。

print arg,行末尾的逗号是什么?
SamK'8

这很容易通过实验或通过检查文档来确定,但可以抑制通常会打印的换行符。
kindall 2012年

5
Python 3打印功能还def verboseprint(*args, **kwargs): print(*args, **kwargs)
带有

61

使用logging模块:

import logging as log

args = p.parse_args()
if args.verbose:
    log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
    log.info("Verbose output.")
else:
    log.basicConfig(format="%(levelname)s: %(message)s")

log.info("This should be verbose.")
log.warning("This is a warning.")
log.error("This is an error.")

所有这些都自动转到stderr

% python myprogram.py
WARNING: This is a warning.
ERROR: This is an error.

% python myprogram.py -v
INFO: Verbose output.
INFO: This should be verbose.
WARNING: This is a warning.
ERROR: This is an error.

有关更多信息,请参见Python Docs教程


8
根据此处的Python文档,仅在正常执行程序时需要打印输出的情况下,不应使用日志记录。看起来这就是OP想要的。
SANDeveloper

1
对于基本问题,这似乎很好,但是许多* nix命令也支持多个级别的详细信息(-v -v -v等),这种方式可能会造成混乱。
TextGeek

12

构建和简化@kindall的答案,这是我通常使用的方法:

v_print = None
def main()
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbosity', action="count", 
                        help="increase output verbosity (e.g., -vv is more than -v)")

    args = parser.parse_args()

    if args.verbosity:
        def _v_print(*verb_args):
            if verb_args[0] > (3 - args.verbosity):
                print verb_args[1]  
    else:
        _v_print = lambda *a: None  # do-nothing function

    global v_print
    v_print = _v_print

if __name__ == '__main__':
    main()

然后,在整个脚本中提供了以下用法:

v_print(1, "INFO message")
v_print(2, "WARN message")
v_print(3, "ERROR message")

您的脚本可以这样调用:

% python verbose-tester.py -v
ERROR message

% python verbose=tester.py -vv
WARN message
ERROR message

% python verbose-tester.py -vvv
INFO message
WARN message
ERROR message

几个注意事项:

  1. 您的第一个参数是错误级别,第二个参数是您的消息。它具有神奇的数字,3可为您的日志记录设置上限,但我接受这是为简便起见而做出的折衷。
  2. 如果要v_print在整个程序中工作,则必须与全局对象打交道。这没什么好玩的,但是我向某人挑战以寻求更好的方法。

1
为什么不将日志记录模块用于INFO和WARN?使用时即导入-v。在您当前的解决方案中,所有内容都将转储到stdout而不是stderr。并且:您通常希望将每个错误传递给用户,不是吗?
Profpatsch 2013年

2
是的,这很公平。日志记录有一些我试图避免的认知开销,但这可能是“正确的”事情。过去让我很烦……
mlissner 2013年

9

我在脚本中所做的是在运行时检查是否设置了“ verbose”选项,然后将日志记录级别设置为debug。如果未设置,则将其设置为info。这样,您就无需对代码进行“如果详细”检查。


2

如果您有一个函数(称为)vprint,它可能会更干净一些,它可以为您检查详细标志。然后,您可以在vprint任何需要可选详细程度的地方调用自己的函数。



2

@kindall的解决方案不适用于我的Python 3.5版。@styles在他的注释中正确指出,原因是附加的可选关键字参数。因此,我对Python 3略加精炼的版本如下所示:

if VERBOSE:
    def verboseprint(*args, **kwargs):
        print(*args, **kwargs)
else:
    verboseprint = lambda *a, **k: None # do-nothing function

1

可能存在一个可能由argparsefrom 设置的全局变量,sys.argv它表示程序是否应该冗长。然后可以编写一个装饰器,使得如果启用了冗长性,那么只要函数运行,标准输入就将被转移到空设备中:

import os
from contextlib import redirect_stdout
verbose = False

def louder(f):
    def loud_f(*args, **kwargs):
        if not verbose:
            with open(os.devnull, 'w') as void:
                with redirect_stdout(void):
                    return f(*args, **kwargs)
        return f(*args, **kwargs)
    return loud_f

@louder
def foo(s):
    print(s*3)

foo("bar")

这个答案受此代码的启发; 实际上,我只是将其用作程序中的一个模块,但遇到了无法理解的错误,因此我对它进行了部分修改。

该解决方案的缺点是,冗长性是二进制的,与之不同的是logging,它允许对程序的冗长程度进行微调。同样,所有 print呼叫都被转移,这可能是不必要的。


0

我需要的是一个打印对象(obj)的函数,但是仅当全局变量verbose为true时,否则它什么都不做。

我希望能够随时更改全局参数“详细”。对我而言,简单性和可读性至关重要。因此,我将按照以下几行内容进行操作:

ak@HP2000:~$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> verbose = True
>>> def vprint(obj):
...     if verbose:
...         print(obj)
...     return
... 
>>> vprint('Norm and I')
Norm and I
>>> verbose = False
>>> vprint('I and Norm')
>>> 

也可以从参数列表中设置全局变量“ verbose”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.