Quando non sai quanti argomenti arriveranno
La maggior parte delle funzioni accetta un numero fisso di parametri. A volte però serve qualcosa di più flessibile: un logger che accetta un numero qualsiasi di messaggi, una funzione wrapper che inoltra a un'altra funzione tutto quello che ha ricevuto, una funzione di plotting che accetta una configurazione senza sapere esattamente quali opzioni userà chi la chiama.
Python risolve il problema con due marcatori speciali nella lista dei parametri: *args e **kwargs.
*args raccoglie gli argomenti posizionali
Un solo asterisco significa "metti tutti gli argomenti posizionali in più in una tupla":
Dentro la funzione, args è una normale tupla. Puoi scorrerla con un ciclo, accedere ai suoi elementi per indice, passarla a len() o farne uno slice.
*args compare di solito insieme a parametri con nome:
first prende il primo argomento; *rest mette tutto il resto in una tupla.
**kwargs raccoglie gli argomenti keyword
Due asterischi fanno la stessa cosa per gli argomenti keyword, raccogliendoli in un dict:
Dentro describe, kwargs è un normale dict. Le chiavi sono i nomi degli argomenti keyword, come stringhe.
Usarli insieme
Puoi usarli entrambi nella stessa funzione. Per convenzione *args viene prima di **kwargs:
L'ordine completo dei parametri, dal primo all'ultimo, è:
- Parametri posizionali normali (obbligatori o con valori predefiniti).
*args.- Parametri solo keyword (tutto ciò che viene dopo
*argsva passato per nome). **kwargs.
title prende il primo argomento posizionale. Gli altri posizionali finiscono in tags. draft va passato per nome (viene dopo *tags). Tutti gli altri argomenti keyword finiscono in metadata.
Spacchettare con * e ** nella chiamata
Gli asterischi funzionano anche al contrario: distribuiscono una sequenza o un dict negli argomenti di una chiamata:
È comodissimo per inoltrare argomenti:
wrapped non ha bisogno di sapere quali argomenti si aspetta log. Raccoglie tutto e lo inoltra. Questo schema compare di continuo nei decoratori (un argomento più avanzato) e nelle funzioni wrapper.
Quando *args e **kwargs sono la scelta sbagliata
È facile farsi prendere la mano e usarli ovunque. Due avvertenze:
Nascondono cosa si aspetta una funzione
Se ogni funzione del tuo codice è def f(*args, **kwargs), chi la chiama non ha idea di quali argomenti siano validi. Usa parametri con nome ogni volta che puoi, e lascia a *args/**kwargs solo gli input davvero variabili o il puro inoltro.
I messaggi di errore diventano vaghi
Un nome keyword scritto male diventa un None silenzioso o un KeyError in fondo alla funzione, invece di un immediato "unexpected keyword argument" nel punto della chiamata. I parametri con nome ti danno un feedback molto migliore.
Come regola: preferisci i parametri con nome, e ricorri a *args/**kwargs solo quando la funzione è davvero flessibile o sta inoltrando argomenti a un altro callable.
Un piccolo esempio pratico
Un helper in stile plotting che avvolge una funzione di terze parti con qualche valore predefinito:
*values permette a chi chiama di passare un numero qualsiasi di elementi; **style assorbe la configurazione in più senza che ogni opzione debba essere un parametro con nome. È flessibile senza essere opaco, perché la logica interna ti dice esattamente quali chiavi legge da style.
Riepilogo
*argsraccoglie gli argomenti posizionali in più in una tupla.**kwargsraccoglie gli argomenti keyword in più in un dict.- Nella chiamata,
*seqe**dictspacchettano nella direzione opposta. - I parametri devono comparire in questo ordine: normali →
*args→ solo keyword →**kwargs. - Non esagerare: quando puoi usarli, i parametri con nome sono più chiari.
Prossimo argomento: lambda, un modo per scrivere al volo piccole funzioni usa e getta.
Domande frequenti
Cos'è *args in Python?
*args raccoglie tutti gli argomenti posizionali in più in una tupla. def f(*args): ti permette di chiamare f(1, 2, 3) e ricevere args come (1, 2, 3). Il nome args è una convenzione: potresti chiamarlo in qualsiasi modo, ma *args è quello che usano tutti.
Cos'è **kwargs in Python?
**kwargs raccoglie tutti gli argomenti keyword in più in un dict. def f(**kwargs): ti permette di chiamare f(name='Ada', age=30) e ricevere kwargs come {'name': 'Ada', 'age': 30}. Insieme, *args e **kwargs permettono a una funzione di accettare qualsiasi combinazione di argomenti.
Devo per forza chiamarli args e kwargs?
No, contano gli asterischi, non i nomi. *values e **options funzionano allo stesso modo. Però args e kwargs sono una convenzione quasi universale nel codice Python: usali, a meno che tu non abbia un motivo preciso per scegliere qualcosa di più descrittivo.