Capire e usare i decoratori Python
I decoratori Python sono un modo per ampliare la funzionalità di fondo di una funzione senza intaccare il codice sorgente alla base.
Cosa sono i decoratori di funzioni e per cosa sono utilizzati?
L’utilizzo dei decoratori Python non è spiegato dettagliatamente in molti tutorial di Python. Questo perché per comprendere i decoratori di funzioni occorre prima conoscere bene le funzioni. Riconoscete i decoratori Python nel codice dall’apposito operatore Python: il carattere “@” seguito dal nome del decoratore di funzioni.
Per maggiori informazioni sui costrutti di programmazione avanzati in Python potete consultare gli altri nostri articoli:
Potete vedere la sintassi di base di una chiamata di un decoratore Python nell’esempio di codice seguente, che tuttavia non implementa alcuna funzionalità:
@decorator
def funzione():
pass
PythonIn questo esempio, il codice inserito in “decorator” sarebbe eseguito quando viene chiamata la funzione denominata “function”.
Spesso i decoratori di funzioni sono usati anche nella programmazione orientata agli oggetti con Python. Ad esempio, il decoratore property in Python è equivalente ai metodi getter e setter in altri linguaggi di programmazione.
Python è un linguaggio di programmazione ideale per i progetti web non soltanto grazie ai pratici costrutti come i decoratori di funzioni. Un altro strumento molto adatto ai progetti web è Deploy Now di IONOS. Deploy Now vi permette di preparare e compilare il vostro progetto facilmente tramite GitHub, tenendo sempre tutto sotto controllo.
Come si usano i decoratori Python
Ampliare la funzionalità base con i decoratori Python
In Python i decoratori sono utilizzati principalmente per aumentare la funzionalità base di una funzione. Una comodità per utilizzare la stessa funzione base in diversi casi d’uso e ampliarla in modo personalizzato. Un semplice ma già eseguibile esempio di codice illustra come usare i decoratori Python per ampliare la funzionalità:
def dec(function):
def foo(x):
print("Prima della chiamata di funzione di " + function.__name__)
function(x)
print("Dopo la chiamata di funzione di " + function.__name__)
return foo
@dec
def bar(y):
print("Chiamata di funzione di bar con il valore " + str(y))
bar("Test")
PythonPer prima cosa nell’esempio di codice viene creato il decoratore chiamato “dec”, che a sua volta contiene una funzione denominata “foo”. Come potete vedere, sostanzialmente un decoratore di funzioni non è nient’altro che una funzione wrapper autonoma, che nel nostro caso contiene un’altra funzione chiamata “foo”. In “foo” viene innanzitutto restituito che ci troviamo prima della chiamata della funzione passata al decoratore nel parametro “function”. Poi la funzione viene eseguita dal parametro. Segue nuovamente una chiamata print in Python che comunica che ci troviamo dopo la chiamata di funzione della funzione passata come parametro.
La seconda parte del codice è costituita da una definizione della funzione chiamata “bar” che riceve un parametro di trasferimento denominato “y”. La funzionalità di bar è facile da comprendere: restituisce sullo schermo la frase “Chiamata di funzione di bar con il valore y”, dove per y è usato il valore passato come parametro. La particolarità della funzione bar è che è stata decorata. Nell’esempio di codice lo riconoscete dalla riga “@dec” prima della definizione di funzione.
Ma di preciso cosa significa che la funzione è stata decorata? Supponiamo di aver tralasciato il decoratore Python, dunque la riga di codice “@dec”. La chiamata di funzione di “bar” che chiude il nostro esempio di codice produrrebbe l’output seguente:
Chiamata di funzione di bar con il valore Test
Qui succede esattamente ciò che ci si aspetta dalla chiamata di funzione: la stringa Python “Test” passata per il parametro y viene inserita nell’istruzione print. L’output della funzione si presenta di conseguenza.
Osserviamo ora l’output della stessa chiamata “bar”, dove questa volta la funzione “bar” è stata decorata con il nostro decoratore Python:
Prima della chiamata di funzione di bar
Chiamata di funzione di bar con il valore Test
Dopo la chiamata di funzione di bar
Ciò che vedete potrebbe forse sorprendervi: dopo essere stata decorata, la nostra funzione non restituisce sullo schermo solo più l’output della propria istruzione print. L’output del nostro decoratore di funzione è stato incapsulato in modo da considerare anche le due istruzioni print della funzione ausiliaria “foo”. Anche la funzionalità base di “bar” è stata estesa aggiungendo altri due output print con il decoratore Python.
Naturalmente questo esempio è artificiale e non persegue una logica di programmazione più profonda. È comunque sufficiente per farsi un’idea del funzionamento dei decoratori Python. Nella funzione decoratore potete ovviamente integrare funzionalità Python a piacere.
Interrogare le condizioni ricorrenti con i decoratori Python
Può capitare che vogliate collegare l’esecuzione di determinate funzioni a una condizione. Probabilmente conoscete già le istruzioni if-else in Python. Ma quando queste condizioni devono essere controllate in un punto diverso, per rendere il codice chiaro può essere utile annidare la condizione in un decoratore Python.
Anche in questo caso un esempio di codice aiuta a comprendere l’utilizzo del decoratore. Alcuni operatori matematici sono definiti solo per i numeri naturali. Sarebbe quindi utile disporre di un decoratore di funzione che verifichi se il parametro di trasferimento di una funzione è un numero naturale.
def numero_naturale(function):
def test(x):
if type(x) == int and x > 0:
return function(x)
else:
raise Exception("L'argomento non è un numero naturale")
@numero_naturale
def fac(n):
if n == 1:
return 1
else:
return n * fac(n-1)
print(fac(5))
print(fac(-1))
PythonNel codice sopra definiamo innanzitutto il nostro decoratore Python chiamato “numero_naturale”, che testa se l’argomento della funzione “function” a esso passata è un numero naturale. A questo scopo, per prima cosa si verifica il tipo di argomento nella condizione if. Si testa inoltre se l’argomento è un numero positivo superiore a 0. Se lo è, viene eseguita la funzione passata al decoratore come parametro. Altrimenti viene generata un’eccezione che comunica che l’argomento della funzione non è un numero naturale.
Per chiarire il funzionamento del nostro decoratore Python, osserviamo la funzione “fac” con cui è decorata. Definita nel codice, viene prima chiamata con il valore “5”, poi col valore “-1”. L’output si presenta come segue:
120
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
fac(-1)
File "<pyshell#11>", line 6, in test
raise Exception("L'argomento non è un numero naturale")
Exception: L'argomento non è un numero naturale
Prima vedete il numero “120”, che corrisponde al fattoriale di 5. Per i numeri naturali funziona quindi la funzione fattoriale. Tuttavia, chiamandola con un numero negativo si genera un errore per via del decoratore Python! Poiché un numero negativo non è un numero naturale, non si deve eseguire la funzione fattoriale.