I de­co­ra­to­ri Python sono un modo per ampliare la fun­zio­na­li­tà di fondo di una funzione senza intaccare il codice sorgente alla base.

Cosa sono i de­co­ra­to­ri di funzioni e per cosa sono uti­liz­za­ti?

L’utilizzo dei de­co­ra­to­ri Python non è spiegato det­ta­glia­ta­men­te in molti tutorial di Python. Questo perché per com­pren­de­re i de­co­ra­to­ri di funzioni occorre prima conoscere bene le funzioni. Ri­co­no­sce­te i de­co­ra­to­ri Python nel codice dall’apposito operatore Python: il carattere “@” seguito dal nome del de­co­ra­to­re di funzioni.

N.B.

Per maggiori in­for­ma­zio­ni sui costrutti di pro­gram­ma­zio­ne avanzati in Python potete con­sul­ta­re gli altri nostri articoli:

Potete vedere la sintassi di base di una chiamata di un de­co­ra­to­re Python nell’esempio di codice seguente, che tuttavia non im­ple­men­ta alcuna fun­zio­na­li­tà:

@decorator
def funzione():
 pass
Python

In questo esempio, il codice inserito in “decorator” sarebbe eseguito quando viene chiamata la funzione de­no­mi­na­ta “function”.

Spesso i de­co­ra­to­ri di funzioni sono usati anche nella pro­gram­ma­zio­ne orientata agli oggetti con Python. Ad esempio, il de­co­ra­to­re property in Python è equi­va­len­te ai metodi getter e setter in altri linguaggi di pro­gram­ma­zio­ne.

Consiglio

Python è un lin­guag­gio di pro­gram­ma­zio­ne ideale per i progetti web non soltanto grazie ai pratici costrutti come i de­co­ra­to­ri 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 fa­cil­men­te tramite GitHub, tenendo sempre tutto sotto controllo.

Come si usano i de­co­ra­to­ri Python

Ampliare la fun­zio­na­li­tà base con i de­co­ra­to­ri Python

In Python i de­co­ra­to­ri sono uti­liz­za­ti prin­ci­pal­men­te per aumentare la fun­zio­na­li­tà base di una funzione. Una comodità per uti­liz­za­re la stessa funzione base in diversi casi d’uso e ampliarla in modo per­so­na­liz­za­to. Un semplice ma già ese­gui­bi­le esempio di codice illustra come usare i de­co­ra­to­ri Python per ampliare la fun­zio­na­li­tà:

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")
Python

Per prima cosa nell’esempio di codice viene creato il de­co­ra­to­re chiamato “dec”, che a sua volta contiene una funzione de­no­mi­na­ta “foo”. Come potete vedere, so­stan­zial­men­te un de­co­ra­to­re di funzioni non è nient’altro che una funzione wrapper autonoma, che nel nostro caso contiene un’altra funzione chiamata “foo”. In “foo” viene in­nan­zi­tut­to re­sti­tui­to che ci troviamo prima della chiamata della funzione passata al de­co­ra­to­re nel parametro “function”. Poi la funzione viene eseguita dal parametro. Segue nuo­va­men­te 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 è co­sti­tui­ta da una de­fi­ni­zio­ne della funzione chiamata “bar” che riceve un parametro di tra­sfe­ri­men­to de­no­mi­na­to “y”. La fun­zio­na­li­tà di bar è facile da com­pren­de­re: re­sti­tui­sce sullo schermo la frase “Chiamata di funzione di bar con il valore y”, dove per y è usato il valore passato come parametro. La par­ti­co­la­ri­tà della funzione bar è che è stata decorata. Nell’esempio di codice lo ri­co­no­sce­te dalla riga “@dec” prima della de­fi­ni­zio­ne di funzione.

Ma di preciso cosa significa che la funzione è stata decorata? Sup­po­nia­mo di aver tra­la­scia­to il de­co­ra­to­re Python, dunque la riga di codice “@dec”. La chiamata di funzione di “bar” che chiude il nostro esempio di codice pro­dur­reb­be l’output seguente:

Chiamata di funzione di bar con il valore Test

Qui succede esat­ta­men­te ciò che ci si aspetta dalla chiamata di funzione: la stringa Python “Test” passata per il parametro y viene inserita nell’istru­zio­ne print. L’output della funzione si presenta di con­se­guen­za.

Os­ser­via­mo ora l’output della stessa chiamata “bar”, dove questa volta la funzione “bar” è stata decorata con il nostro de­co­ra­to­re 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 sor­pren­der­vi: dopo essere stata decorata, la nostra funzione non re­sti­tui­sce sullo schermo solo più l’output della propria istru­zio­ne print. L’output del nostro de­co­ra­to­re di funzione è stato in­cap­su­la­to in modo da con­si­de­ra­re anche le due istru­zio­ni print della funzione au­si­lia­ria “foo”. Anche la fun­zio­na­li­tà base di “bar” è stata estesa ag­giun­gen­do altri due output print con il de­co­ra­to­re Python.

Na­tu­ral­men­te questo esempio è ar­ti­fi­cia­le e non persegue una logica di pro­gram­ma­zio­ne più profonda. È comunque suf­fi­cien­te per farsi un’idea del fun­zio­na­men­to dei de­co­ra­to­ri Python. Nella funzione de­co­ra­to­re potete ov­via­men­te integrare fun­zio­na­li­tà Python a piacere.

In­ter­ro­ga­re le con­di­zio­ni ri­cor­ren­ti con i de­co­ra­to­ri Python

Può capitare che vogliate collegare l’ese­cu­zio­ne di de­ter­mi­na­te funzioni a una con­di­zio­ne. Pro­ba­bil­men­te conoscete già le istru­zio­ni if-else in Python. Ma quando queste con­di­zio­ni devono essere con­trol­la­te in un punto diverso, per rendere il codice chiaro può essere utile annidare la con­di­zio­ne in un de­co­ra­to­re Python.

Anche in questo caso un esempio di codice aiuta a com­pren­de­re l’utilizzo del de­co­ra­to­re. Alcuni operatori ma­te­ma­ti­ci sono definiti solo per i numeri naturali. Sarebbe quindi utile disporre di un de­co­ra­to­re di funzione che verifichi se il parametro di tra­sfe­ri­men­to 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))
Python

Nel codice sopra definiamo in­nan­zi­tut­to il nostro de­co­ra­to­re 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 con­di­zio­ne if. Si testa inoltre se l’argomento è un numero positivo superiore a 0. Se lo è, viene eseguita la funzione passata al de­co­ra­to­re come parametro. Al­tri­men­ti viene generata un’eccezione che comunica che l’argomento della funzione non è un numero naturale.

Per chiarire il fun­zio­na­men­to del nostro de­co­ra­to­re Python, os­ser­via­mo 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 cor­ri­spon­de al fat­to­ria­le di 5. Per i numeri naturali funziona quindi la funzione fat­to­ria­le. Tuttavia, chia­man­do­la con un numero negativo si genera un errore per via del de­co­ra­to­re Python! Poiché un numero negativo non è un numero naturale, non si deve eseguire la funzione fat­to­ria­le.

Vai al menu prin­ci­pa­le