Як реалізувати параметр --verbose або -v у сценарії?


94

Я знаю , --verboseчи -vз декількох інструментів , і я хотів би реалізувати це в деякі з моїх власних сценаріїв та інструментів.

Я думав розмістити:

if verbose:
    print ...

через мій вихідний код, так що якщо користувач передає -vопцію, для змінної verboseбуде встановлено значення, Trueа текст буде надруковано.

Це правильний підхід чи існує більш поширений спосіб?

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


9
Чому б не використовувати модуль ведення журналу та не встановити рівень журналу INFO за замовчуванням, а DEBUG при передачі --verbose? Краще не застосовувати нічого, що вже доступне мовою ...
Тім,

3
@Tim, я згоден, але модуль реєстрації журналу досить болючий.
mlissner

Відповіді:


106

Моя порада - використовувати функцію. Але замість того, щоб вводити 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

5
Функція друку Python 3 також бере необов’язковий аргумент ключового слова, щоб повністю відтворити функціональність друку:def verboseprint(*args, **kwargs): print(*args, **kwargs)
lstyls

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 та навчальні посібники .


8
Відповідно до Документів Python тут , ведення журналу не слід використовувати в тих випадках, коли Вам потрібно лише надрукувати вихідні дані при звичайному виконанні програми. Схоже, саме цього хоче ОП.
SAND розробник 03.03.16

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

2
Так, це справедливо. У лісозаготівлі є деякі когнітивні накладні витрати, яких я намагався уникнути, але це, мабуть, "правильна" справа. Це мене просто дратувало в минулому ...
mlissner

9

Що я роблю в своїх сценаріях, це перевіряю під час виконання, чи встановлено параметр 'детальний', а потім встановлюю рівень налагодження для налагодження. Якщо він не встановлений, я встановлюю його як інформацію. Таким чином, у вас немає перевірки "якщо багатослівно" по всьому коду.


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

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 є істинною, інакше вона нічого не робить.

Я хочу мати можливість будь-коли змінити глобальний параметр "детальний". Простота та читабельність для мене надзвичайно важливі. Тож я б продовжував, як вказують наступні рядки:

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')
>>> 

Глобальну змінну "детально" можна також встановити зі списку параметрів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.