Python *args e **kwargs spiegati: Funzioni flessibili con esempi chiari
Quelle stelle nelle firme delle funzioni sono meno misteriose di quanto sembri. Scopri come *args e **kwargs raccolgono argomenti, come le stesse stelle li scompattano e come progettare firme di funzioni difficili da utilizzare in modo improprio.
*argsraccoglie argomenti posizionali aggiuntivi in una tupla.**kwargsraccoglie argomenti di parole chiave aggiuntivi in un dizionario.- I nomi sono solo una convenzione, poiché ciò che conta sono
*e**. - Le stesse stelle decomprimono una lista o un dizionario quando chiami una funzione.
Prima o poi leggerete una funzione come def handler(*args, **kwargs) e vi chiederete cosa accetta. Python *args e **kwargs consentono a una funzione di accettare un numero flessibile di argomenti. Alimentano decoratori, wrapper e molte API di libreria.
Questa guida spiega come funzionano in entrambe le direzioni, insieme ai nuovi modi in cui Python ti consente di controllare come viene chiamata una funzione. Abbiamo eseguito ogni esempio su Python 3.13 e l’output mostrato proviene da quell’esecuzione.
Cosa fa *args
Un parametro scritto con una stella raccoglie eventuali argomenti posizionali aggiuntivi in una tupla:
def total(*args):
print(type(args).__name__, args)
return sum(args)
print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6
print(total())
# tuple ()
# 0
Senza argomenti, ottieni semplicemente una tupla vuota, quindi la funzione funziona ancora. Il nome args è una convenzione. *numbers si comporterebbe allo stesso modo e un nome più descrittivo è spesso migliore.
Cosa fa **kwargs
Due stelle raccolgono eventuali argomenti di parole chiave aggiuntivi in un dizionario:
def describe(**kwargs):
print(type(kwargs).__name__, kwargs)
describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}
Poiché è un normale dizionario, puoi scorrerlo, cercare le chiavi con get() e trasmetterlo. Le chiavi mantengono l’ordine in cui sono state passate.
Ordine dei parametri
Quando mescoli tipi di parametri, Python richiede un ordine specifico:
- Parametri posizionali regolari
*arg- Parametri relativi solo alle parole chiave
**kwargs
def order(item, qty=1, *extras, priority=False, **notes):
print(item, qty, extras, priority, notes)
order("pen")
# pen 1 () False {}
order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}
Avviso priority. Poiché viene dopo *extras, può essere impostato solo per nome, quindi un argomento posizionale vagante non potrebbe mai impostarlo per sbaglio. Non è consentito inserire **kwargs prima e il nostro test ha sollevato un SyntaxError: ”gli argomenti non possono seguire l’argomento var-parola chiave”.
Disimballaggio durante la chiamata
Le stesse stelle funzionano al contrario nel luogo della chiamata. Una stella distribuisce una lista o una tupla in argomenti posizionali e due stelle distribuiscono un dizionario in argomenti di parole chiave:
nums = [4, 5, 6]
total(*nums) # same as total(4, 5, 6) -> 15
settings = {"name": "Ben", "city": "Delhi"}
describe(**settings) # same as describe(name="Ben", city="Delhi")
Il disimballaggio funziona con qualsiasi funzione, non solo con quelle che utilizzano *args e **kwargs. Ciò è utile quando la configurazione arriva come dizionario:
def connect(host, port, timeout=5):
return f"{host}:{port} (timeout {timeout})"
connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'
connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'
Questo errore è utile. Una funzione senza **kwargs rifiuta le opzioni sconosciute, quindi un errore di battitura nel nome di un’impostazione fallisce rumorosamente invece di essere ignorato silenziosamente.
Argomenti basati solo su parole chiave
Un semplice * nella firma, senza nome, significa “tutto ciò che segue deve essere passato per parola chiave”:
def make_user(name, *, admin=False):
return name, admin
make_user("Asha", admin=True)
# ('Asha', True)
make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given
Questo è un ottimo modo per proteggere le bandiere booleane. Una chiamata come make_user("Asha", True) costringe i lettori a indovinare cosa significa True. La richiesta di admin=True rende ogni chiamata autoesplicativa.
Argomenti solo posizionali
A partire da Python 3.8, un / contrassegna i parametri che possono essere passati solo per posizione:
def area(width, height, /):
return width * height
area(3, 4) # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
# passed as keyword arguments: 'width, height'
Ciò consente a una libreria di rinominare tali parametri in un secondo momento senza violare il codice di nessuno, poiché nessuno può dipendere dai nomi. Diversi built-in funzionano in questo modo. Ad esempio, len(obj=[1]) non funziona.
Suggerimento: Il modello completo è def f(pos_only, /, normal, *, kw_only). Raramente sono necessari tutti e tre, ma sapere cosa significano / e * ti aiuta a leggere la documentazione e le firme delle biblioteche.
Decoratori e involucri
Questo è il motivo principale nel mondo reale per usarli entrambi insieme. Un wrapper spesso deve accettare qualunque argomento venga accettato dalla funzione originale e trasmetterlo senza modifiche:
import functools, time
def timed(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
return result
return wrapper
@timed
def slow_add(a, b, delay=0.01):
time.sleep(delay)
return a + b
print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5
Non è necessario che il wrapper conosca affatto i parametri di slow_add, quindi funziona per qualsiasi funzione. La cifra temporale sarà leggermente diversa. La nostra guida ai decoratori Python spiega functools.wraps e altri dettagli.
Opzioni di fusione
** funziona anche all’interno di un dizionario letterale, che fornisce un modo accurato per applicare sostituzioni oltre alle impostazioni predefinite:
defaults = {"theme": "light", "lang": "en"}
def build(**overrides):
return {**defaults, **overrides}
build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}
Le chiavi successive prevalgono, quindi l’override sostituisce l’impostazione predefinita. Anche le sottoclassi utilizzano questo modello, come in super().__init__(*args, **kwargs), per passare argomenti aggiuntivi alla classe genitore. Vedi Classi e oggetti Python per ulteriori informazioni su super().
Foglio informativo
| Sintassi | Dove | Significato |
|---|---|---|
def f(*argomenti) |
Definizione | Raccogli argomenti posizionali aggiuntivi in una tupla |
def f(**kwargs) |
Definizione | Raccogli argomenti di parole chiave extra in un dict |
def f(a, *, b) |
Definizione | b è solo per parole chiave |
def f(a, /, b) |
Definizione | a è solo posizionale |
f(*items) |
Chiama | Distribuire una sequenza in argomenti posizionali |
f(**options) |
Chiama | Diffondi un dict in argomenti di parole chiave |
{**a, **b} |
Letterale | Unisci dizionari |
[*a, *b] |
Letterale | Combina gli iterabili in un elenco |
Quando evitarli
La flessibilità ha un prezzo. Una funzione definita come def create(**kwargs) non dice ai lettori quali opzioni esistono e gli editor non possono completarle automaticamente. Gli errori di battitura possono passare silenziosamente attraverso diversi livelli prima che qualcosa fallisca.
Quindi preferisci i parametri espliciti quando sai di cosa ha bisogno una funzione. Usa *args e **kwargs quando hai veramente bisogno di flessibilità: wrapper, decoratori, inoltro di argomenti a un’altra funzione o un vero input di lunghezza variabile come total(*numbers). Se una funzione necessita di molte opzioni con nome, anche un dataclass può essere più chiaro.
Errori comuni
Utilizzando un valore predefinito modificabile
Non si tratta delle stelle, ma spesso appare vicino a loro. Un valore predefinito viene creato una volta, quando la funzione viene definita, e quindi condiviso tra le chiamate:
def bad(items=[]):
items.append(1)
return items
print(bad(), bad())
# [1, 1] [1, 1]
Entrambe le chiamate restituirono lo stesso elenco, che a quel punto conteneva due elementi. Utilizzare items=None e creare un nuovo elenco all’interno della funzione.
Altri errori
- Dimenticarsi di disfare i bagagli. Chiamando
total(nums)si passa un elenco anziché tre numeri. Scrivitotal(*nums). - Errore di battitura. Una funzione che accetta
**kwargsma ignora i tasti sconosciuti nasconde le opzioni con errori di ortografia. Convalidarli o evitarli**kwargs. - Passando lo stesso argomento due volte.
f(1, **{"a": 2}), doveaè il primo parametro, genera un errore di “valori multipli”. - Non inoltrato in un wrapper. La chiamata a
func(args, kwargs)passa una tupla e un dict. La forma corretta èfunc(*args, **kwargs).
Digita suggerimenti per *args e **kwargs
I suggerimenti di tipo su *args e **kwargs descrivono ogni singolo valore, non l’intera raccolta. Ciò sorprende molte persone:
def total(*prices: float) -> float:
return sum(prices) # prices is tuple[float, ...]
def tag(**attrs: str) -> str:
return " ".join(f'{k}="{v}"' for k, v in attrs.items()) # dict[str, str]
total(1.5, 2.5) # 4.0
tag(id="x", role="button") # id="x" role="button"
Qui prices viene trattato come una tupla di numeri in virgola mobile e attrs come un dizionario di stringhe. Quando gli argomenti delle parole chiave hanno tipi diversi, Python 3.12 ha aggiunto un’opzione precisa: a TypedDict combinato con Unpack.
from typing import TypedDict, Unpack
class Options(TypedDict, total=False):
timeout: float
retries: int
def fetch(url: str, **opts: Unpack[Options]) -> None: ...
Un controllore di tipo come mypy o Pyright può quindi rifiutare fetch(url, retries="3") o un nome di opzione scritto in modo errato, cosa che un semplice **kwargs non potrà mai fare. Il comportamento in fase di esecuzione, tuttavia, non cambia. Quando abbiamo eseguito fetch("u", retries="3") con Python 3.13, è stato eseguito senza problemi, perché Python stesso non applica i suggerimenti sul tipo.
Se ti ritrovi a documentare un lungo elenco di chiavi accettate per **kwargs, valuta la possibilità di sostituirle con parametri espliciti composti solo da parole chiave. Gli editor possono quindi completarli automaticamente e gli errori vengono rilevati immediatamente.
Considerazioni finali
Ricorda le due direzioni. In una definizione, le stelle si raccolgono. Durante una chiamata, disfano le valigie. Aggiungi argomenti composti solo da parole chiave per rendere le chiamate più chiare e riserva **kwargs per il codice che inoltra realmente gli argomenti. Per esercitarti, scrivi un piccolo decoratore che registri gli argomenti di ogni chiamata e usalo su alcune funzioni diverse.
Domande frequenti
Qual è la differenza tra *args e **kwargs?
*args raccoglie argomenti posizionali aggiuntivi in una tupla. **kwargs raccoglie argomenti di parole chiave extra in un dizionario. Una funzione può utilizzarli entrambi insieme.
Devo chiamarli arg e kwarg?
No. Le stelle fanno il lavoro, quindi *valori o **opzioni sono ugualmente validi. args e kwargs sono semplicemente una convenzione ampiamente riconosciuta.
In che ordine dovrebbero andare *args e **kwargs?
Prima i parametri regolari, poi *args, poi i parametri solo con parole chiave e infine **kwargs. Qualsiasi altro ordine provoca un SyntaxError.
Cosa significa un singolo * nella definizione di una funzione?
Un * nudo indica che ogni parametro successivo deve essere passato tramite parola chiave. Rende le chiamate più chiare, soprattutto per le bandiere booleane.
Cosa significa / significa in una funzione Python?
I parametri prima di / sono solo posizionali, quindi i chiamanti non possono passarli per nome. Questa funzionalità è stata aggiunta in Python 3.8.
Continua a imparare
Hai bisogno di aiuto con test, WordPress o SEO?
Aiutiamo i team a fornire software migliore e a far crescere i loro siti. Raccontaci a cosa stai lavorando e ti suggeriremo chiaramente il prossimo passo.
Altre guide su test, intelligenza artificiale e sviluppo moderno
Ti è piaciuto questo articolo? Scegli un argomento e passa direttamente ai nostri tutorial più recenti.
Spiegazione delle allucinazioni dell'intelligenza artificiale: perché i modelli linguistici inventano le cose e come ridurle
Spiegazione delle allucinazioni dell'intelligenza artificiale: cosa sono, perché i modelli linguistici le producono, tipi comuni, come ridurle con b…
Leggi l'articolo →DevOpsSpiegazione delle variabili d'ambiente: file .env, segreti e configurazione eseguiti correttamente
Variabili d'ambiente spiegate con esempi Python che abbiamo eseguito: lettura e impostazione, perché ogni valore è una stringa, file .env, ereditari…
Leggi l'articolo →Sviluppo softwareSpiegazione della limitazione della velocità API: algoritmi, intestazioni e gestione degli errori 429
La limitazione della velocità dell'API spiegata con una simulazione che abbiamo eseguito: bucket di token, finestra fissa, finestra scorrevole, risp…
Leggi l'articolo →Automazione dei testDerisione in Python: unittest.mock, patch e gli errori che fanno mentire i test
Mocking in Python con unittest.mock, testato: Mock e return_value, side_effect, patch dove viene utilizzato un nome, specifiche e autospec, date moc…
Leggi l'articolo →Test del softwareSpiegazione della copertura del codice: copertura di linea o di filiale e cosa significa realmente il 100%.
Copertura del codice spiegata con una vera esecuzione di cover.py: copertura di linea, ramo e condizione, lettura di un rapporto, perché la copertur…
Leggi l'articolo →Database e SQLDELETE vs TRUNCATE vs DROP in SQL: differenze, rollback e quando utilizzarli
DELETE vs TRUNCATE vs DROP spiegato con SQL testato: cosa rimuove, rollback, trigger, reimpostazione dell'identità, velocità e come differiscono Pos…
Leggi l'articolo →Database e SQLSpiegazione delle visualizzazioni SQL: crea, utilizza ed evita le insidie, con esempi
Viste SQL spiegate con le query che abbiamo eseguito: creazione di una vista, risultati in tempo reale, nascondere colonne, prestazioni, viste aggio…
Leggi l'articolo →Sviluppo softwareInterfaccia TypeScript e tipo: differenze reali, testate con il compilatore
Interfaccia TypeScript vs tipo, verificata con il vero compilatore: cosa possono fare entrambi, cosa può fare solo uno, fusione delle dichiarazioni,…
Leggi l'articolo →Sviluppo softwareSpiegazione di Python *args e **kwargs: funzioni flessibili con esempi chiari
Python *args e **kwargs spiegati con il codice che abbiamo eseguito: compressione e decompressione, ordine dei parametri, argomenti solo per parole …
Leggi l'articolo →Sviluppo softwareFunzioni Lambda Python: sintassi, usi reali e quando non usarli
Funzioni lambda Python spiegate con il codice che abbiamo eseguito: sintassi, ordinamento con chiave, mappa e filtro, chiusure, trap dell'associazio…
Leggi l'articolo →Intelligenza artificialeCosa sono gli agenti AI? Come funzionano, con un semplice ciclo di agenti che puoi eseguire
Cosa sono gli agenti IA? Una semplice guida al ciclo dell'agente, agli strumenti, alla memoria e ai guardrail, con un piccolo ciclo Python che abbia…
Leggi l'articolo →DevOpsMonitoraggio vs osservabilità: la vera differenza, con log, metriche e tracce
Spiegazione del monitoraggio e dell'osservabilità: errori noti e sconosciuti, log, metriche e tracce, una procedura dettagliata sugli incidenti di p…
Leggi l'articolo →DevOpsVolumi Docker e montaggi di associazione: mantenere i dati del contenitore nel modo giusto
Volumi Docker e montaggi di collegamento spiegati: perché i dati del contenitore scompaiono, volumi denominati, montaggi di collegamento, tmpfs, ese…
Leggi l'articolo →Automazione dei testTDD vs BDD: differenze chiave, una caratteristica costruita in entrambi i modi e quando utilizzarli ciascuno
TDD vs BDD spiegato con una funzione di sconto costruita in entrambi i modi: refactoring rosso-verde con il codice che abbiamo eseguito, scenari "Da…
Leggi l'articolo →Sviluppo softwareGit merge vs rebase: differenze, esempi e quando usare ciascuno
Git merge vs rebase spiegato con output reale del log, una tabella di confronto, la regola d'oro del rebase e un flusso di lavoro sicuro per i team.
Leggi l'articolo →Sviluppo softwareList comprehension in Python: sintassi, esempi e quando evitarla
List comprehension in Python spiegata: sintassi, filtri, if-else, cicli annidati, tempi misurati e i casi in cui un normale ciclo for è meglio.
Leggi l'articolo →Database e SQLSQL JOIN spiegati con esempi (Inner, Left, Right, Full)
Capisci i JOIN SQL passo dopo passo con tabelle e query di esempio: INNER, LEFT, RIGHT, FULL, CROSS e SELF, con errori comuni e consigli sulle prest…
Leggi l'articolo →