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.


Condividi sui Social