Как реализовать в скрипте опцию --verbose или -v?

95

Я знаю --verboseили -vиз нескольких инструментов, и я хотел бы реализовать это в некоторых из моих собственных скриптов и инструментов.

Думал разместить:

if verbose:
    print ...

через мой исходный код, так что, если пользователь передает -vпараметр, переменная verboseбудет установлена ​​в значение, Trueи текст будет напечатан.

Это правильный подход или есть более распространенный способ?

Дополнение: я не прошу способа реализовать синтаксический анализ аргументов. Я знаю, как это делается. Меня особенно интересует только подробный вариант.

Aufwind
источник
9
Почему бы не использовать модуль ведения журнала и не установить уровень журнала INFO по умолчанию и DEBUG, когда передается --verbose? Лучше не реализовывать заново то, что уже доступно на языке ...
Тим
3
@ Тим, согласен, но модуль логирования довольно болезненный.
mlissner

Ответы:

109

Мое предложение - использовать функцию. Но вместо того, чтобы вставлять 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уже есть функция (или если вы хотите использовать printкак функцию в 2.x, используя from __future__ import print_function), это еще проще:

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

Таким образом, функция определяется как бездействующая, если подробный режим выключен (с использованием лямбда), вместо постоянного тестирования 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
Это легко определить экспериментально или проверить документацию, но он подавляет разрыв строки, который обычно печатается.
kindall 07
5
Функция печати Python 3 также принимает необязательный аргумент ключевого слова, чтобы полностью воспроизвести функциональность печати:def verboseprint(*args, **kwargs): print(*args, **kwargs)
lstyls
63

Используйте 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 и руководства .

Профпатч
источник
8
Согласно документации Python здесь , ведение журнала не следует использовать в тех случаях, когда вам нужно только распечатать вывод при нормальном выполнении программы. Похоже, это то, чего хочет ОП.
SANDeveloper, 03
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работать над всей своей программой, вам придется делать мусор с помощью global. Это не весело, но я призываю кого-нибудь найти лучший способ.
Млисснер
источник
1
Почему бы вам не использовать модуль ведения журнала для ИНФОРМАЦИИ и ПРЕДУПРЕЖДЕНИЯ? То есть импорт, когда -vиспользуется. В вашем текущем решении все выгружается в stdout вместо stderr. И: обычно вы хотите передавать пользователю каждую ошибку, не так ли?
Profpatsch
2
Да, это справедливо. Ведение журнала имеет некоторые когнитивные издержки, которых я пытался избежать, но, вероятно, это «правильный» поступок. Раньше это меня просто раздражало ...
mlissner
9

В своих сценариях я проверяю во время выполнения, установлен ли параметр «подробный», а затем устанавливаю уровень ведения журнала для отладки. Если он не установлен, я устанавливаю его на info. Таким образом, у вас не будет проверок «если подробный» по всему вашему коду.

Джоунси
источник
2

Было бы проще, если бы у вас была функция, скажем, вызываемая vprint, которая проверяет за вас подробный флаг. Затем вы просто вызываете свою собственную vprintфункцию в любом месте, где хотите, по желанию.

Ли-Ман
источник
2

Я украл код регистрации из virtualenv для своего проекта. Посмотрите на main()о , virtualenv.pyчтобы увидеть , как она инициализируется. Код посыпают logger.notify(), logger.info(), logger.warn()и тому подобное. Какие методы фактически Испустите выход определяется , был ли virtualenv вызывается с -v, -vv, -vvvили -q.

Джордж В. Рейли
источник
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
Stefanct
источник
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")

Этот ответ вдохновлен этим кодом ; на самом деле, я собирался просто использовать его как модуль в своей программе, но у меня были ошибки, которые я не мог понять, поэтому я адаптировал его часть.

Обратной стороной этого решения является то, что подробность является двоичной, в отличие от with logging, что позволяет более точно настраивать степень подробности программы. Кроме того, все print вызовы переадресовываются, что может быть нежелательным.

Даниэль Диниз
источник
0

Что мне нужно, так это функция, которая печатает объект (obj), но только если глобальная переменная verbose истинна, иначе она ничего не делает.

Я хочу иметь возможность изменить глобальный параметр "подробный" в любое время. Для меня простота и удобочитаемость имеют первостепенное значение. Итак, я бы продолжил, как указывают следующие строки:

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" также можно установить из списка параметров.

user377367
источник