Tabella dei contenuti: Mostra/Chiudi
Se hai già programmato in Python, avrai visto sicuramente alcuni parametri di una funzione con nomi strani tipo *args
e **kwargs
. In questo post andrò a spiegarti tutto ciò che devi sapere a riguardo!
Per capire a pieno il significato e l'utilizzo di questi due argomenti, è consigliabile avere delle basi sulle funzioni, liste e dizionari. In alternativa qui troverai i migliori libri per imparare a programmare in Python.
Detto ciò iniziamo subito!
Cosa serve e come usare *args
*args ci permette di passare potenzialmente un numero illimitato e variabile di parametri alla nostra funzione, args infatti è l'abbreviazione di arguments.
Prendiamo ad esempio questa funzione che accetta tre parametri in ingresso:
def moltiplica(a, b, c):
return a * b * c
print(moltiplica(2, 4, 13))
#output
104
La nostra funzione è corretta ma se volessimo andare a moltiplicare più o meno numeri ci sarebbe un problema in quanto essa ne accetta solo tre.
Per risolvere questo problema possiamo scrivere, al posto dei tre parametri, la nostra istruzione *
seguita dal nome che vogliamo assegnarli(args e kwargs infatti sono solo nomi di default) per indicare che quella funzione accetta un numero di parametri variabile:
def moltiplica(*numeri_da_moltiplicare):
risultato = 1
for numero in numeri_da_moltiplicare:
risultato *= numero
return risultato
print(moltiplica(2, 4, 13, 10, 12, 33))
print(moltiplica(3, 5))
print(moltiplica(10, 14, 2))
#output
348
15
280
I parametri vengono passati come una tupla e in questo caso il simbolo *
viene definito operatore di unpacking proprio perché "spacchetta" tutte le variabili che vengono passate alla funzione.
Da non confondere l'operatore di unpacking con il simbolo *
utilizzato invece per eseguire una moltiplicazione.
Cosa serve e come usare **kwargs
**kwargs invece sta per keyword arguments, è molto simile ad *args, con la differenza che i parametri non vengono raggruppati in una tupla, ma in un dizionario contenente tutti i parametri passati con nome chiave (tradotto è orrendo, meglio chiamarli keyword arguments).
def info_pizza(**pizza):
for key, value in pizza.items():
print("{0} = {1}".format(key, value))
info_pizza(nome="margherita", prezzo=5, ingredienti=["pomodoro", "mozzarella"])
#output
nome = margherita
prezzo = 5
ingredienti = ['pomodoro', 'mozzarella']
Utilizzare gli operatori di unpacking al di fuori delle funzioni
In Python possiamo utilizzare gli operatori * e ** non solo per definire dei parametri in una funzione, ma in qualsiasi contesto.
Ad esempio:
ingredienti = ['pomodoro', 'mozzarella', 'patatine fritte', 'wurstel']
print(*ingredienti)
#output
pomodoro mozzarella patatine fritte wurstel
O addirittura possiamo fare l'operazione inversa a quanto abbiamo visto inizialmente, ovvero usare **kwargs per chiamare una funzione e non più per ottenere i parametri:
def info_pizza(nome, prezzo, ingredienti):
print("Nome = {0}".format(nome))
print("Prezzo = {0}".format(prezzo))
print("Ingredienti = {0}".format(ingredienti))
ingredienti = ['pomodoro', 'mozzarella', 'patatine fritte', 'wurstel']
info = {"nome": "margherita", "prezzo": 5, "ingredienti": ingredienti}
info_pizza(**info)
#output
Nome = margherita
Prezzo = 5
Ingredienti = ['pomodoro', 'mozzarella', 'patatine fritte', 'wurstel']
Attenzione! nel dizionario info
, le key devono avere lo stesso nome dei parametri che la nostra funzione vuole in ingresso(nome, prezzo, ingredienti).
Ordine e precedenza dei parametri
Nella definizione dei parametri della funzione bisogna seguire un ordine, ovvero parametri normali(narg
), parametri opzionali(oarg
), *args
, **kwargs
:
def una_funzione(narg, oarg=None, *args, **kwargs):
bla
bla
...
Casi d'uso principali
Una delle situazioni nella quale *args
e **kwargs
vengono utilizzati principalmente per le sottoclassi:
class Parent(object):
def __init__(self, value1, value2):
# bla
# ...
print(value1, value2)
class Child(Parent):
def __init__(self, *args, **kwargs):
# bla
# ...
print("myfoo")
super().__init__(*args, **kwargs)
In questo modo puoi estendere il comportamento della classe Parent senza saperne molto della sua struttura.
Un altro caso d'uso comune è in quello dei decoratori:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
print("Durata {0}: {1:.5f} secondi".format(func.__name__,
time.time() - start))
return wrapper
@timer
def funzione(a, b):
c = a + b
time.sleep(2.3)
print(c)
@timer
def funzione_piu_lunga(a, b, c, d):
e = a + b + c + d
time.sleep(5)
print(e)
funzione(3, 5)
funzione_piu_lunga(1, 2, 3, 4)
In questo script viene creato un decoratore che stampa a schermo il tempo di esecuzione delle funzioni. Come puoi vedere vengono utilizzati i parametri *kwarg e **kwargs per chiamare la nostra funzione senza dover sapere esattamente quali e quanti parametri vuole in ingresso.