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

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

Я думал поставить:

if verbose:
    print ...

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

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

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


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


Ответы (9)


Мое предложение состоит в том, чтобы использовать функцию. Но вместо того, чтобы помещать 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 в функции), но поскольку вы устанавливаете его с помощью команды -line, вам нужно принять решение только один раз.

Затем вы используете, например. verboseprint("look at all my verbosity!", object(), 3) всякий раз, когда вы хотите напечатать "подробное" сообщение.

person kindall    schedule 12.05.2011
comment
Еще лучше сделать это как функцию print: принять много аргументов. Он может быть реализован как print(*args) в 3.x и как for arg in args: print arg, в 2.x. Основное преимущество в том, что он позволяет смешивать строки и вещи других типов в одном сообщении без явных str вызовов/форматирования и конкатенаций. - person ; 12.05.2011
comment
Для чего нужна запятая в конце строки print arg,? - person SamK; 07.08.2012
comment
Это легко определить самостоятельно экспериментально или проверив документацию, но он подавляет разрыв строки, который обычно печатается. - person kindall; 07.08.2012
comment
Функция печати Python 3 также принимает необязательный аргумент ключевого слова, поэтому для полного воспроизведения функциональности печати: def verboseprint(*args, **kwargs): print(*args, **kwargs) - person lstyls; 04.06.2016
comment
Почему следует использовать этот подход? Здесь совершенно не нужна оптимизация производительности. Вызов пустой функции занимает на моей машине 55,6 нс, функция, которая ничего не делает, кроме как если False занимает 61,8 нс. На практике это не актуально. Но, с другой стороны, глобально заданные параметры всегда являются препятствием для модульного тестирования. - person lumbric; 29.12.2020
comment
@lumbric Этот подход заключается не в оптимизации производительности, а в том, чтобы не писать глупый код. Если вы знаете уровень детализации при запуске вашей программы и знаете, что он не изменится, бессмысленно постоянно его тестировать. - person Jeyekomon; 03.02.2021
comment
@Jeyekomon, вы также можете возразить, что бессмысленно постоянно вызывать пустую функцию. Может быть трудно судить объективно, но лично мне легче читать, если if находится внутри функции. - person lumbric; 03.02.2021

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

person Profpatsch    schedule 14.03.2013
comment
Согласно документам Python здесь, ведение журнала не должно использоваться в тех случаях, когда вам требуется только распечатать вывод при нормальном выполнении программы. Похоже, это то, чего хочет ОП. - person SANDeveloper; 03.03.2016
comment
Это кажется подходящим для основной проблемы, но многие команды *nix также поддерживают несколько уровней детализации (-v -v -v и т. д.), что может привести к путанице. - person TextGeek; 09.04.2019

Построение и упрощение ответа @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. Это не весело, но я призываю кого-нибудь найти лучший способ.
person mlissner    schedule 08.02.2013
comment
Почему вы не используете модуль ведения журнала для INFO и WARN? То есть импортировать его, когда используется -v. В вашем текущем решении все сбрасывается на стандартный вывод вместо стандартного вывода. И: обычно вы хотите передавать пользователю каждую ошибку, не так ли? - person Profpatsch; 14.03.2013
comment
Да, это справедливое замечание. Ведение журнала связано с некоторыми когнитивными издержками, которых я пытался избежать, но, вероятно, это правильно. Просто раньше меня это раздражало... - person mlissner; 14.03.2013

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

person jonesy    schedule 12.05.2011

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

person Lee-Man    schedule 12.05.2011
comment
это действительно простое элегантное решение. Спасибо! - person Ahsin Shabbir; 23.05.2021

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

person George V. Reilly    schedule 07.06.2013

решение @kindall не работает с моей версией Python 3.5. @styles правильно указывает в своем комментарии, что причиной является дополнительный необязательный аргумент keywords. Следовательно, моя слегка доработанная версия для Python 3 выглядит так:

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

Там может быть глобальная переменная, вероятно, установленная с argparse из 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 вызовы, что может быть нежелательно.

person Daniel Diniz    schedule 04.06.2018

Что мне нужно, так это функция, которая печатает объект (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" также можно задать из списка параметров.

person user377367    schedule 29.12.2015