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

132 投票
9 回答
195070 浏览
提问于 2025-04-16 17:30

我知道很多工具都有 --verbose-v 这个选项,我想把这个功能加到我自己的一些脚本和工具里。

我想过把以下代码放在我的源代码里:

if verbose:
    print ...

这样如果用户输入 -v 这个选项,变量 verbose 就会被设置为 True,然后就会打印出相关的文本。

这样做对吗?还是说有更常见的方法呢?

补充说明:我并不是在问如何解析参数。我知道怎么做。我只是特别想了解 verbose 这个选项。

9 个回答

19

在简化@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在整个程序中都能用,你就得处理一下全局变量。虽然这不太好玩,但我挑战大家找出更好的方法。
78

使用 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 文档教程

136

我的建议是使用一个函数。不过,不要把if放在函数里面,虽然你可能会想这样做,而是这样写:

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语句里定义一个函数,只有当条件为真时,这个函数才会被定义!)

如果你使用的是Python 3,print已经是一个函数了(或者如果你愿意在2.x版本中使用print作为函数,可以用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)

如果你愿意并且能够在启动“生产”版本的脚本时使用Python的-O标志来开启和关闭详细模式(或者设置PYTHONOPTIMIZE环境变量),那么更好的方法是在你想打印详细输出的地方测试__debug__标志:

if __debug__: print("Verbosity enabled")

在没有优化的情况下,这些语句会被执行(而if会被去掉,只留下语句的主体)。在优化运行时,Python会完全去掉这些语句。它们对性能没有任何影响!想了解更多,可以查看我关于__debug__-O的博客文章:我的博客

撰写回答