Corso Python / Raccogliere ed espandere argomenti con *args e **kwargs

Nella definizione di una funzione, *args raccoglie argomenti posizionali aggiuntivi in una tupla e **kwargs raccoglie argomenti keyword aggiuntivi in un dizionario. Nella chiamata, * e ** svolgono il percorso inverso: espandono una collezione in argomenti distinti.

Distinguere raccolta ed espansione

Args e kwargs sono nomi convenzionali, non parole riservate: è la sintassi degli asterischi a determinarne il ruolo. Nell’esempio si usano amounts e fields perché descrivono meglio i dati. Rendi variabile il numero di argomenti solo quando corrisponde al problema; una firma con parametri espliciti rende più visibili opzioni e possibili errori.

Punto d’uso Espressione Comportamento
Definizione def f(*args) Raccoglie ulteriori argomenti posizionali in una tupla.
Definizione def f(**kwargs) Raccoglie ulteriori argomenti keyword in un dizionario.
Chiamata f(*values) Passa gli elementi di un iterabile come argomenti posizionali.
Chiamata f(**options) Passa le voci di una mappatura con chiavi stringa come argomenti keyword.

Le regole ufficiali delle liste di argomenti variabili completano quelle degli argomenti posizionali, keyword e predefiniti.

Eseguire una somma variabile e descrivere opzioni

Salva il programma completo in UTF-8 come varargs.py, dentro una cartella di esercitazione. Esegui python varargs.py con l’interprete del progetto selezionato. Fuori dall’ambiente virtuale su macOS o Ubuntu usa python3 se è il comando dell’interprete scelto. Non servono pacchetti esterni.

def total(*amounts, fee=0):
    return sum(amounts) + fee

def describe(**fields):
    return ", ".join(f"{key}={value}" for key, value in sorted(fields.items()))

values = [3500, 8000, 2500]
print(total(*values, fee=500))
options = {"category": "food", "currency": "KRW"}
print(describe(**options))

Il risultato atteso è:

14500
category=food, currency=KRW

La chiamata total(*values, fee=500) espande i tre importi e li raccoglie nel parametro amounts come tupla. La loro somma è 14000; aggiungendo fee si ottiene 14500. Fee è keyword-only, perché viene dopo il parametro che raccoglie i posizionali: per specificarlo devi usare il suo nome.

describe(**options) espande category e currency come keyword. La funzione li raccoglie in fields, ordina le coppie per chiave e unisce le rappresentazioni chiave=valore. Qui currency è soltanto un campo descrittivo: non calcola una conversione valutaria.

Riconoscere duplicati e forme di espansione errate

Le chiavi di una mappatura espansa con ** devono essere stringhe. Devono anche essere accettate dalla funzione: un parametro esplicito deve corrispondere al nome, oppure la funzione deve prevedere un parametro per raccogliere keyword aggiuntive. La specifica delle chiamate descrive i vincoli.

Passare describe(category="food", **{"category": "books"}) genera TypeError: la stessa keyword viene fornita due volte. Non avviene la sostituzione dell’ultimo valore tipica di alcune operazioni di costruzione o unione dei dizionari. Una keyword duplicata nella chiamata resta un errore anche quando la funzione usa **fields.

Con *"123" vengono espansi i caratteri "1", "2" e "3", non viene creato l’intero 123. total(*"123") non esegue quindi la somma numerica attesa e genera TypeError. Allo stesso modo total(values) passa la lista come un solo argomento, mentre total(*values) passa tre numeri distinti.

Un wrapper può raccogliere con def wrapper(*args, **kwargs) e inoltrare a un’altra funzione con func(*args, **kwargs). Passare kwargs senza i due asterischi significherebbe invece fornire un dizionario come un unico argomento posizionale. Controlla di non aggiungere una keyword già presente nei dati inoltrati.

Mantenere controllata l’interfaccia accettata

Accettare keyword arbitrarie non verifica automaticamente la loro correttezza. Describe, per esempio, accetta anche un nome scritto male: questo può essere utile per un descrittore generico ma nascondere un errore in un’interfaccia che si aspetta campi precisi. Prima di inoltrare opzioni esterne ad API o driver di database, valida nomi consentiti e campi obbligatori.

  1. Aggiungi zero a values: il totale con commissione resta 14500, pur aumentando il numero degli importi.
  2. Ripristina values e ometti fee=500: si applica il valore predefinito zero e il totale diventa 14000.
  3. Prova la keyword category sia esplicita sia espansa da un dizionario e verifica il TypeError.
  4. Ripristina il programma originale e confronta entrambe le righe con il risultato atteso.

Una funzione con un singolo parametro lista può essere più chiara quando la collezione rappresenta un unico dato concettuale. Usa gli argomenti variabili quando modellano davvero il compito e conserva parametri nominati espliciti per le opzioni dal significato definito.