Spiegazione di Python *args e **kwargs: funzioni flessibili con esempi chiari

  1. Home
  2. Blog
  3. Sviluppo software

Sviluppo software

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.

8 min di lettura
Spiegazione di Python *args e **kwargs: funzioni flessibili con esempi chiari

Punti chiave

  • *args raccoglie argomenti posizionali aggiuntivi in una tupla.
  • **kwargs raccoglie 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:

  1. Parametri posizionali regolari
  2. *arg
  3. Parametri relativi solo alle parole chiave
  4. **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. Scrivi total(*nums).
  • Errore di battitura. Una funzione che accetta **kwargs ma 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}), dove a è 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

Lavora con Stack Quora

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.

Continua a leggere

Altre guide su test, intelligenza artificiale e sviluppo moderno

Ti è piaciuto questo articolo? Scegli un argomento e passa direttamente ai nostri tutorial più recenti.

Intelligenza artificiale

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 →
DevOps

Spiegazione 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 software

Spiegazione 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 test

Derisione 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 software

Spiegazione 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 SQL

DELETE 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 SQL

Spiegazione 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 software

Interfaccia 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 software

Spiegazione 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 software

Funzioni 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 artificiale

Cosa 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 →
DevOps

Monitoraggio 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 →
DevOps

Volumi 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 test

TDD 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 software

Git 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 software

List 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 SQL

SQL 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 →