Video Corso Intermedio Python 3

10: Il Modulo ArgParse pt.2

Menu della Serie




La nostra calcolatrice funziona piuttosto bene, anche se abbastanza minimalista. I parametri utilizzati fin'ora dallo script, come avete letto nella descrizione dell'help, sono parametri posizionali o di posizione, necessari quindi al corretto funzionamento dello script. Per ottenere una versione un pó più verbosa e ricca, come nell'esempio a inizio spiegazione dobbiamo aggiungere dei parametri così detti opzionali, perché il programma funziona comunque anche senza di loro. Qualora non vengano specificati, a questi viene di fatti assegnato il None Value.

I parametri opzionali possono avere due versioni, e sono contraddistinti dall'utilizzo del trattino - per la versione abbreviata [come per -h] e dal doppio trattino -- nel caso della versione estesa [come per --help]

Procediamo ad aggiugere un parametro opzionale:

parser.add_argument("-v", "--verbose", help="Restituisce output verboso.", action="store_true")


Di default, l'azione associata è semplicemente "store", che significa archiviare, mettere in memoria; action="store_true" significa che vogliamo depositare il valore True nella variabile verbose quando questa viene passata come parametro. Salvando un valore Booleano in questa maniera fa si che ci si riferisca spesso a questi parametri come flags. Detto ció, a questo punto possiamo implementare l'opzione in questa maniera:

risultato = calcolatrice(args.n1, args.n2, args.operazione)
if args.verbose:
    print(f"Il risultato dell'operazione '{args.operazione}' è: {risultato}")
else:
    print(risultato)

Quindi, analizziamo anzitutto il messaggio di aiuto.

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py --help
usage: calc_argparse.py [-h] [-v] n1 n2 {add,sot,mol,div}

Semplice calcolatrice per addizioni, sottrazioni, moltiplicazioni e divisioni

positional arguments:
  n1                 Primo Numero
  n2                 Secondo Numero
  {add,sot,mol,div}  Tipo di Operazione

optional arguments:
  -h, --help         show this help message and exit
  -v, --verbose      Restituisce output verboso.

Ecco che tra gli argomenti opzionali abbiamo anche verbose. Testiamolo subito:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 333 111 sot -v
Il risultato dell'operazione è: 222.0

E a conferma della loro natura opzionale, proviamo la stessa operazione ma senza questo parametro:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 333 111 sot
222.0



Abbiamo accennato che, per i parametri opzionali, l'azione di default è store, che significa immagazzinare, archiviare, mettere in memoria. Ció significa che in effetti, oltre ad utilizzare i parametri opzionali come flags, possiamo utilizzarli anche per depositarci dei valori aggiuntivi. Ad esempio, facciamo una piccola modifica al codice:

(...)

parser.add_argument("-v", "--verbose", help="Restituisce output verboso.")
(...)
if args.verbose:
    print(f"Il risultato dell'operazione '{args.operazione}' è: {risultato}")
    print("Verbose:", args.verbose)

e lanciamo lo script...

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 11 3 mol --verbose 555
Il risultato dell'operazione è: 33.0
Verbose: 555

Stessa cosa sarebbe successa chiaramente se avessi passato una stringa di caratteri testuali:

pymike@harley:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 11 3 mol --verbose /spam/eggs/bacon
Il risultato dell'operazione è: 33.0
Verbose: /spam/eggs/bacon

Com'è facile immaginare, questo può tornarci utile in svariati casi. Per restare in tema con la nostra calcolatrice, possiamo passare a verbose un intero e tramite questo selezionare diversi livelli di verbosità dell'output:

(...)
parser.add_argument("-v", "--verbose", help="Restituisce output verboso.", type=int, choices=[1,2])
args = parser.parse_args()

risultato = calcolatrice(args.n1, args.n2, args.operazione)

if args.verbose == 1:
    print(f"Il risultato dell'operazione '{args.operazione}' è: {risultato}")
elif args.verbose == 2:
    print("Benvenuti al programma Calcolatrice!")
    print(f"Il risultato dell'operazione '{args.operazione}' tra '{args.n1}' e '{args.n2}' è: {risultato}")
else:
    print(f"Il risultato dell'operazione è: {risultato}")

Testiamo il messaggio di aiuto:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py --help
usage: calc_argparse.py [-h] [-v {1,2}] n1 n2 {add,sot,mol,div}

Semplice calcolatrice per addizioni, sottrazioni, moltiplicazioni e divisioni

positional arguments:
  n1                    Primo Numero
  n2                    Secondo Numero
  {add,sot,mol,div}     Tipo di Operazione

optional arguments:
  -h, --help            show this help message and exit
  -v {1,2}, --verbose {1,2}
                        Restituisce output verboso.

E ora il programma:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 90 9 div -v 2
Benvenuti al programma Calcolatrice!
Il risultato dell'operazione 'div' tra '90.0' e '9.0' è: 10.0



In questo caso specifico possiamo utilizzare anche "count" ovvero un'altra action associabile molto utile e impiegata spesso proprio per aumentare i livelli di verbosità.

parser.add_argument("-v", "--verbose", help="Restituisce output verboso", action="count")

Ora verrà depositato un numero pari a quelle che sono le volte che richiamiamo il parametro opzionale, ad esempio se facessi "-vvv" verrebbe associato il valore 3. Quindi modifico anche le clausole di controllo cambiando "== 2" con ">= 2":

elif args.verbose >= 2:
    print("Benvenuti al programma calcolatrice!")
    print(f"Il risultato dell'operazione '{args.operazione}' tra '{args.n1}' e '{args.n2}' è: {risultato}")

Ora posso testare l'opzione:

pymike@harley:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 33 11 add -v
Il risultato dell'operazione 'add' è: 44.0

pymike@harley:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 33 11 add -vv
Benvenuti al programma calcolatrice!
Il risultato dell'operazione 'add' tra '33.0' e '11.0' è: 44.0

Il programma funzione benone! peró, se ci dimentichiamo di passare almeno un "-v" otteniamo errore! Questo perché con action="count", se non passiamo almeno una volta il parametro, a questo viene associato il None Value.

pymike@harley:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 33 11 add
Traceback (most recent call last):
  File "./calc_argparse.py", line 28, in <module>
    elif args.verbose >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'

Possiamo aggirare il problema passando un valore di default, in modo che se decidiamo di usare il programma in modalitá non verbosa, venga associato pur sempre un intero:

parser.add_argument("-v", "--verbose", help="Restituisce output verboso", action="count", default=0)



Per completezza, dopo aver mostrato parametri posizionali e opzionali, parliamo ora di argomenti ad esclusione reciproca, in inglese "Mutually Exclusive Arguments", ovvero parametri che fanno parte di uno stesso gruppo, in cui possiamo quindi sceglierne solamente uno. Nell'esempio della nostra semplice calcolatrice, diciamo che vogliamo definire oltre all'opzione "--verbose" anche un'opzione "--quiet", che ci mostra semplicemente il risultato senza nessun'altra informazione.

Dobbiamo quindi anzitutto creare un gruppo, per convenzione lo chiamaremo "group", e quindi aggiungere a questo gruppo i parametri opzionali associati, e quindi modificare il resto del codice in maniera appropriata:

group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", help="Restituisce output verboso.", type=int, choices=[1,2])
group.add_argument("-q", "--quiet", help="Output non verboso", action="store_true")
args = parser.parse_args()

risultato = calcolatrice(args.n1, args.n2, args.operazione)

if args.quiet:
    print(risultato)
elif args.verbose == 1:
    print(f"Il risultato dell'operazione '{args.operazione}' è: {risultato}")
elif args.verbose == 2:
    print("Benvenuti al programma Calcolatrice!")
    print(f"Il risultato dell'operazione '{args.operazione}' tra '{args.n1}' e '{args.n2}' è: {risultato}")
else:
    print(f"Il risultato dell'operazione è: {risultato}")

Bene, proviamo ora a richiamare la funzione help sul nostro script:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py -h
usage: calc_argparse.py [-h] [-v {1,2} | -q] n1 n2 {add,sot,mol,div}

Semplice calcolatrice per addizioni, sottrazioni, moltiplicazioni e divisioni

positional arguments:
  n1                    Primo Numero
  n2                    Secondo Numero
  {add,sot,mol,div}     Tipo di Operazione

optional arguments:
  -h, --help            show this help message and exit
  -v {1,2}, --verbose {1,2}
                        Restituisce output verboso.
  -q, --quiet           Output non verboso

Quindi, se lanciamo il programma con il flag quite attivo:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 90 9 div -q
10.0

Mentre, chiaramente, trattandosi di parametri ad esclusione reciproca, se chiamiamo sia verbose che quite, otteniamo un errore:

pymike@programmareinpython:~/Desktop/lezione_argparse$ python3.6 ./calc_argparse.py 90 9 div -q -v 2
usage: calc_argparse.py [-h] [-v {1,2} | -q] n1 n2 {add,sot,mol,div}
calc_argparse.py: error: argument -v/--verbose: not allowed with argument -q/--quiet



Come al solito, Happy Coding!