Come implementare l'opzione --verbose o -v in uno script?


94

Conosco --verboseo-v da diversi strumenti e mi piacerebbe implementarlo in alcuni dei miei script e strumenti.

Ho pensato di posizionare:

if verbose:
    print ...

attraverso il mio codice sorgente, in modo che se un utente passa l' -vopzione, la variabile verboseverrà impostata su Truee il testo verrà stampato.

È questo l'approccio giusto o esiste un modo più comune?

Aggiunta: non sto chiedendo un modo per implementare l'analisi degli argomenti. Che so come si fa. Sono interessato solo specialmente all'opzione dettagliata.


9
Perché non utilizzare il modulo di registrazione e impostare il livello di registro INFO per impostazione predefinita e DEBUG quando viene passato --verbose? Meglio non reimplementare tutto ciò che è già disponibile nella lingua ...
Tim

3
@ Tim, sono d'accordo, ma il modulo di registrazione è piuttosto doloroso.
mlissner

Risposte:


106

Il mio suggerimento è di utilizzare una funzione. Ma invece di inserire la iffunzione, cosa che potresti essere tentato di fare, fallo in questo modo:

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

(Sì, puoi definire una funzione in un file if un'istruzione e verrà definita solo se la condizione è vera!)

Se stai usando Python 3, dove printè già una funzione (o se sei disposto a usarlo printcome funzione in 2.x usando from __future__ import print_function) è ancora più semplice:

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

In questo modo, la funzione viene definita come non fare nulla se la modalità dettagliata è disattivata (utilizzando un lambda), invece di testare costantemente il verbose flag.

Se l'utente potesse cambiare la modalità di verbosità durante l'esecuzione del programma, questo sarebbe l'approccio sbagliato (avresti bisogno del fileif nella funzione), ma poiché lo stai impostando con un flag della riga di comando, devi solo prendere la decisione una volta.

Quindi si utilizza, ad esempio, verboseprint("look at all my verbosity!", object(), 3)ogni volta che si desidera stampare un messaggio "dettagliato".


1
Ancora meglio, fallo come la printfunzione: accetta molti argomenti. Può essere implementato come print(*args)in 3.xe come for arg in args: print arg,in 2.x. Il vantaggio principale è che consente di mescolare stringhe e cose di altri tipi in un messaggio senza strchiamate / formattazione e concatenazioni esplicite .

A cosa serve la virgola alla fine della print arg,riga?
SamK

Questo è facilmente determinabile sperimentalmente o controllando la documentazione, ma sopprime l'interruzione di riga che normalmente verrebbe stampata.
kindall

5
La funzione di stampa di Python 3 accetta anche l'argomento della parola chiave opzionale, quindi per riprodurre completamente la funzionalità di stampa:def verboseprint(*args, **kwargs): print(*args, **kwargs)
lstyls

61

Usa il loggingmodulo:

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.")

Tutti questi vanno automaticamente a 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.

Per ulteriori informazioni, vedere la documentazione di Python e i tutorial .


8
Come per Python Docs qui , la registrazione non dovrebbe essere utilizzata nei casi in cui si richiede solo di stampare l'output nella normale esecuzione del programma. Sembra che sia quello che vuole l'OP.
SANDeveloper

1
Questo sembra a posto per il problema di base, ma molti comandi * nix supportano anche più livelli di verbosità (-v -v -v, ecc.), Che potrebbero creare confusione in questo modo.
TextGeek

12

Costruire e semplificare la risposta di @ kindall, ecco cosa uso di solito:

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

Questo fornisce quindi il seguente utilizzo in tutto lo script:

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

E il tuo script può essere chiamato in questo modo:

% 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

Un paio di note:

  1. Il tuo primo argomento è il tuo livello di errore e il secondo è il tuo messaggio. Ha il numero magico di3 che imposta il limite superiore per la registrazione, ma lo accetto come un compromesso per la semplicità.
  2. Se vuoi v_printlavorare durante il tuo programma, devi fare la spazzatura con il globale. Non è divertente, ma sfido qualcuno a trovare un modo migliore.

1
Perché non usi il modulo di registrazione per INFO e WARN? Questo è importarlo quando -vviene utilizzato. Nella tua soluzione attuale tutto viene scaricato su stdout invece che su stderr. E: normalmente vuoi trasmettere ogni errore all'utente, non è vero?
Profpatsch

2
Sì, questo è un punto giusto. La registrazione ha un sovraccarico cognitivo che stavo cercando di evitare, ma probabilmente è la cosa "giusta" da fare. Mi ha solo infastidito in passato ...
mlissner

9

Quello che faccio nei miei script è controllare in fase di esecuzione se l'opzione "verbose" è impostata, quindi impostare il mio livello di registrazione su debug. Se non è impostato, lo imposto su info. In questo modo non hai controlli "se verbose" in tutto il codice.


2

Potrebbe essere più pulito se hai una funzione, diciamo chiamata vprint, che controlla il flag verbose per te. Quindi chiami la tua vprintfunzione in qualsiasi posto desideri la verbosità opzionale.



2

La soluzione di @ kindall non funziona con la mia versione 3.5 di Python. @styles afferma correttamente nel suo commento che il motivo è l' argomento aggiuntivo delle parole chiave opzionali . Quindi la mia versione leggermente raffinata per Python 3 assomiglia a questo:

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

1

Potrebbe esserci una variabile globale, probabilmente impostata con argparsefrom sys.argv, che indica se il programma deve essere dettagliato o meno. Quindi un decoratore potrebbe essere scritto in modo tale che se la verbosità fosse attiva, l'input standard verrebbe deviato nel dispositivo nullo fintanto che la funzione dovesse essere eseguita:

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

Questa risposta è ispirata da questo codice ; in realtà, stavo per usarlo come modulo nel mio programma, ma ho ricevuto errori che non riuscivo a capire, quindi ho adattato una parte di esso.

Lo svantaggio di questa soluzione è che la verbosità è binaria, a differenza di logging, il che consente una messa a punto più precisa di quanto può essere verboso il programma. Inoltre, tutte le print chiamate vengono deviate, il che potrebbe essere indesiderato.


0

Ciò di cui ho bisogno è una funzione che stampa un oggetto (obj), ma solo se la variabile globale verbose è vera, altrimenti non fa nulla.

Voglio essere in grado di modificare il parametro globale "verbose" in qualsiasi momento. La semplicità e la leggibilità per me sono di fondamentale importanza. Quindi procederei come indicano le seguenti righe:

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

Anche la variabile globale "verbose" può essere impostata dalla lista dei parametri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.