1.3.2 - Interfacce a riga di comando (argparse)¶
Il corretto sviluppo di uno script Python implica la necessità di evitare modifiche al codice sorgente ogni volta che dobbiamo cambiare un parametro (ad esempio, il numero di epoche di addestramento, il percorso del dataset, o il learning rate del modello). In tal senso, il modulo argparse della libreria standard ci permette di passare questi parametri direttamente da riga di comando (CLI).
Uno script con argparse¶
L'uso di argparse prevede quattro step fondamentali:
- Per prima cosa, dovremo creare un oggetto di classe
ArgumentParser. - A questo punto, dovremo aggiungere gli argomenti attesi dello script (
add_argument). - Chiameremo quindi il metodo
parse_argsper effettuare il parsing degli argomenti attesi al punto 2. - Utilizzeremo infine i valori di cui è stato fatto il parsing all'interno dello script.
Proviamo a fare un esempio. Immaginiamo di voler popolare una semplice struttura dati che rappresenta una persona. Per farlo, utilizzeremo una dataclass.
from dataclasses import dataclass
@dataclass
class Persona:
nome: str
cognome: str
eta: int
studente: bool
A questo punto, possiamo creare un nuovo script, che chiameremo run.py, e definire una funzione main che accetta gli argomenti di cui è stato fatto il parsing, creando contestualmente l'oggetto.
import argparse
# ...
def run(args: argparse.Namespace):
"""
Logica principale dello script.
Riceve i parametri parsati e istanzia la classe.
"""
p = Persona(
nome=args.nome,
cognome=args.cognome,
eta=args.eta,
studente=args.studente
)
print(f"Creato oggetto: {p}")
if __name__ == '__main__':
# 1. Creazione del parser
parser = argparse.ArgumentParser(description="Script demo per gestire persone.")
# 2. Aggiunta argomenti
# Argomento con valore di default (Opzionale)
parser.add_argument(
'-n', '--nome',
help='Nome della persona',
default='Mario'
)
# Argomento obbligatorio (required=True)
parser.add_argument(
'-c', '--cognome',
help='Cognome della persona',
required=True
)
# 3. Parsing
args = parser.parse_args()
# 4. Esecuzione
run(args)
A questo punto possiamo eseguire lo script da terminale:
python run.py
# Errore!: manca il cognome (obbligatorio)
# Output: error: the following arguments are required: -c/--cognome
python run.py -c Rossi
# Funzionamento corretto
# Output: Creato oggetto: Persona(nome='Mario', cognome='Rossi', ...)
Help automatico
Il modulo argparse genera automaticamente un messaggio di aiuto. Per vederlo, proviamo a lanciare lo script usando la dicitura python run.py --help
Gestione del tipo degli argomenti con type¶
Di default, argparse tratta tutti gli argomenti passati come delle stringhe. Qualora vogliamo passare un numero (ad esempio, l'età), dovremo specificarne il tipo mediante l'argomento type:
parser.add_argument(
'-a', '--age',
help='Età della persona',
type=int, # Conversione automatica in intero
default=18
)
Se provassimo a passare un valore non numerico, lo script si fermerà mostrando a schermo un errore esplicativo.
Flag Booleani con store_true¶
Spesso abbiamo bisogno di alcuni "flag comportamentali" che fungano da interruttori on/off, come ad esempio --use-gpu, --verbose, o --dry-run. In questi casi non è necessario passare un valore, ma soltanto sapere se il flag è presente o meno: per farlo, potremo usare l'argomento action='store_true'. Ad esempio:
parser.add_argument(
'-s', '--student',
help='Indica se la persona è uno studente',
action='store_true' # Se presente diventa True, altrimenti False
)
Utilizzo:
# Senza flag -> studente=False
python run.py -c Rossi
# Con flag -> studente=True
python run.py -c Rossi --studente
Scelte vincolate con choices¶
Immaginiamo di avere un parametro che può assumere soltanto un insieme limitato di valori. In questo caso, possiamo usare il parametro choices:
parser.add_argument(
'--role',
choices=['admin', 'user', 'guest'],
default='user',
help='Ruolo nel sistema'
)
Se passiamo, ad esempio, --role superadmin, argparse bloccherà l'esecuzione segnalando che il valore non è tra quelli ammessi.
Esempio Completo¶
Chiudiamo con uno esempio di codice "pronto all'uso" ed in grado di coprire molti dei casi d'uso che possiamo trovare in un progetto di machine learning.
import argparse
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# Iperparametri (Numeri)
parser.add_argument(
'--epochs',
type=int,
default=10,
help='Numero di epoche')
parser.add_argument(
'--lr',
type=float,
default=0.001,
help='Learning rate')
# Percorsi (Stringhe)
parser.add_argument(
'--data',
type=str,
required=True,
help='Path del dataset')
# Configurazioni (Scelte e Flag)
parser.add_argument(
'--model',
choices=['resnet', 'vgg'],
ùdefault='resnet')
parser.add_argument(
'--gpu',
action='store_true',
help='Abilita training su GPU')
args = parser.parse_args()
print(f"Training {args.model} on {args.data} for {args.epochs} epochs...")
if args.gpu:
print("GPU Enabled!")
if __name__ == '__main__':
main()
Con questo esempio abbiamo quindi un vero e proprio template che possiamo seguire per i nostri esperimenti.