Python *args e **kwargs
Scopri come *args e **kwargs permettono alle funzioni Python di accettare qualsiasi numero di argomenti posizionali e keyword, con esempi pratici.
*args e **kwargs sono una sintassi speciale di Python che consente a una funzione di accettare un numero variabile di argomenti. *args raccoglie gli argomenti posizionali extra in una tupla, mentre **kwargs raccoglie gli argomenti keyword extra in un dizionario. Insieme offrono la massima flessibilità — puoi scrivere funzioni che funzionano con uno o cento argomenti.
Questa pagina tratta entrambe le funzionalità in profondità: come funzionano, quando usarle, come combinarle e le insidie più comuni da evitare.
Cos'è *args?
Quando si prefissa il nome di un parametro con un singolo asterisco (*), Python raccoglie tutti gli argomenti posizionali extra passati alla funzione in una tupla associata a quel nome di parametro. Il nome args è una convenzione — si potrebbe scrivere *numbers o *values — ma *args è universalmente riconosciuto.
def add_all(*args):
total = 0
for n in args:
total += n
return total
print(add_all(1, 2, 3)) # 6
print(add_all(10, 20, 30, 40)) # 100
print(add_all()) # 0All'interno della funzione, args è una tupla ordinaria su cui puoi iterare, indicizzare o passare ad altre funzioni. Chiamare add_all() con zero argomenti è valido — args è semplicemente una tupla vuota.
Combinare parametri normali con *args
I parametri normali (posizionali) vengono prima; *args cattura tutto ciò che segue:
def greet(greeting, *names):
for name in names:
print(greeting + ', ' + name + '!')
greet('Hello', 'Alice', 'Bob', 'Charlie')
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!greeting viene riempito dal primo argomento; names riceve il resto come tupla. Se chiami greet('Hi') senza nomi aggiuntivi, names è una tupla vuota e il ciclo semplicemente non viene eseguito — nessun errore.
Cos'è **kwargs?
Due asterischi (**) prima del nome di un parametro indicano a Python di raccogliere tutti gli argomenti keyword extra in un dizionario. Anche qui, kwargs è una convenzione; qualsiasi identificatore Python valido funziona.
def describe(**kwargs):
for key, value in kwargs.items():
print(key + ': ' + str(value))
describe(name='Alice', age=30, city='New York')
# name: Alice
# age: 30
# city: New YorkAll'interno della funzione, kwargs è un dizionario ordinario. Puoi iterarvi sopra, cercare chiavi o passarlo ad altre funzioni. Il chiamante decide quali chiavi fornire — nessuna di esse è fissa nella definizione della funzione.
Quando usare **kwargs
**kwargs eccelle quando:
- Una funzione deve accettare un insieme flessibile e aperto di opzioni con nome (configurazione, metadati, attributi HTML).
- Stai scrivendo un wrapper che deve inoltrare argomenti keyword a un'altra funzione senza sapere quali siano.
- Vuoi costruire un dizionario da argomenti keyword in modo leggibile (evita il codice ripetitivo di
dict(key=value, ...)).
Combinare *args e **kwargs
Una singola funzione può accettare argomenti posizionali illimitati e argomenti keyword illimitati. L'ordine richiesto nella firma è:
- Parametri posizionali normali
*args- Parametri keyword-only (con valori predefiniti)
**kwargs
def log_event(event, *tags, **metadata):
print('Event:', event)
print('Tags:', tags)
print('Metadata:', metadata)
log_event('login', 'auth', 'user', user_id=42, ip='127.0.0.1')
# Event: login
# Tags: ('auth', 'user')
# Metadata: {'user_id': 42, 'ip': '127.0.0.1'}event riceve il primo argomento posizionale; tags cattura i restanti argomenti posizionali; metadata cattura tutti gli argomenti keyword.
Spacchettamento degli argomenti con * e **
Gli operatori * e ** non servono solo nelle definizioni di funzione — funzionano anche sul lato della chiamata per spacchettare sequenze e mapping in argomenti separati.
Spacchettamento di una lista o tupla con *
def multiply(a, b, c):
return a * b * c
nums = [2, 3, 4]
print(multiply(*nums)) # 24*nums spacchetta la lista in modo che a=2, b=3, c=4. Equivale a scrivere multiply(2, 3, 4). Vedi Unpack Tuples per ulteriori informazioni sull'operatore di spacchettamento.
Spacchettamento di un dizionario con **
def power(base, exp):
return base ** exp
params = {'base': 3, 'exp': 4}
print(power(**params)) # 81**params mappa ogni chiave del dizionario al nome del parametro corrispondente. Questo è utile quando gli argomenti sono memorizzati in un dizionario di configurazione costruito a runtime.
Argomenti keyword-only dopo *args
Qualsiasi parametro elencato dopo *args nella firma può essere passato solo per nome (diventa un argomento keyword-only). È un modo pulito per aggiungere flag opzionali senza ambiguità:
def configure(host, *args, port=80, debug=False):
print('host:', host)
print('extra:', args)
print('port:', port)
print('debug:', debug)
configure('localhost', 'arg1', port=8080, debug=True)
# host: localhost
# extra: ('arg1',)
# port: 8080
# debug: Trueport e debug non possono essere impostati posizionalmente perché *args consuma già tutto l'overflow posizionale. Questo schema è comune nelle API di libreria — gli utenti devono scrivere esplicitamente port=8080, il che rende i siti di chiamata auto-documentati.
Per una spiegazione dettagliata delle regole di scoping di Python, consulta Python Scope.
Inoltro degli argomenti a un'altra funzione
Uno degli usi più pratici di *args/**kwargs è scrivere wrapper e decoratori che passano gli argomenti a una funzione interna senza sapere quali siano:
def add_all(*args):
return sum(args)
def wrapper(*args, **kwargs):
print('Calling with args:', args, 'kwargs:', kwargs)
return add_all(*args)
print(wrapper(1, 2, 3))
# Calling with args: (1, 2, 3) kwargs: {}
# 6Questo schema compare in tutta la libreria standard di Python ed è il fondamento dei decoratori e delle funzioni di ordine superiore.
Ordine completo della firma
Python impone una regola di ordinamento rigorosa per tutti i tipi di parametri. L'ordine completo è:
| Posizione | Tipo | Esempio |
|---|---|---|
| 1 | Solo posizionale (Python 3.8+) | a, b, / |
| 2 | Normale posizionale-o-keyword | x, y |
| 3 | Posizionale variabile | *args |
| 4 | Keyword-only | flag=True |
| 5 | Keyword variabile | **kwargs |
Violare questo ordine provoca un SyntaxError. Una funzione che usa tutti e cinque i tipi appare così:
def full_sig(pos1, pos2, /, normal, *args, kw_only, **kwargs):
print(pos1, pos2, normal, args, kw_only, kwargs)
full_sig(1, 2, 3, 4, 5, kw_only='k', extra='e')
# 1 2 3 (4, 5) k {'extra': 'e'}Nel codice quotidiano raramente si ha bisogno di tutti e cinque insieme. Gli schemi più comuni sono *args da solo, **kwargs da solo, o *args seguito da **kwargs.
Annotazioni di tipo
Puoi annotare *args e **kwargs con type hint. L'annotazione si applica a ogni singolo elemento, non alla tupla o al dizionario nel suo insieme:
from typing import Any
def add_all(*args: float) -> float:
return sum(args)
def describe(**kwargs: Any) -> None:
for key, value in kwargs.items():
print(f'{key}: {value}')
print(add_all(1.5, 2.5, 3.0)) # 7.0
describe(name='Bob', score=99)
# name: Bob
# score: 99*args: float significa che ogni elemento di args dovrebbe essere un float. **kwargs: Any significa che i valori possono essere qualsiasi cosa. Questo mantiene soddisfatti gli strumenti di analisi statica pur preservando la flessibilità a runtime.
Insidie comuni
1. Ordine errato degli argomenti nella firma
Mettere **kwargs prima di *args è un SyntaxError:
# Wrong — raises SyntaxError
# def bad(name, **kwargs, *args): ...
# Correct
def good(name, *args, **kwargs):
pass2. Modificare la tupla args
args è una tupla e quindi immutabile. Se hai bisogno di modificare gli argomenti, converti prima in una lista:
def double_all(*args):
items = list(args) # mutable copy
items = [x * 2 for x in items]
return items
print(double_all(1, 2, 3)) # [2, 4, 6]3. Oscurare il nome di un parametro obbligatorio
Se usi *args e hai anche un argomento keyword con lo stesso nome di un parametro posizionale, i chiamanti possono confondersi. Mantieni i nomi dei parametri distinti e usa i parametri keyword-only (dopo *args) per i flag opzionali.
4. Abuso di **kwargs al posto di parametri espliciti
**kwargs nasconde ciò che una funzione accetta effettivamente, rendendo più difficili il completamento automatico e l'analisi statica. Preferisci parametri espliciti per le opzioni che la funzione supporta realmente; usa **kwargs solo quando l'insieme di opzioni è davvero aperto o quando si inoltrano argomenti a un'altra funzione.
Riepilogo
| Funzionalità | Sintassi | Cosa raccoglie | Tipo dentro la funzione |
|---|---|---|---|
| Args posizionali variabili | *args | Argomenti posizionali extra | tuple |
| Args keyword variabili | **kwargs | Argomenti keyword extra | dict |
| Spacchettamento sequenza alla chiamata | func(*seq) | Lista/tupla → args posizionali | — |
| Spacchettamento mapping alla chiamata | func(**mapping) | Dict → args keyword | — |
Per argomenti strettamente correlati, vedi Python Functions per le basi delle funzioni, Python Lambda per le funzioni anonime e Python Scope per come Python risolve i nomi delle variabili.