Python decorator: cosa sono e come funzionano

di Lorenzo Neri
281 visualizzazioni

Cosa sono i decorator e come funzionano nel linguaggio Python? In questo articolo lo lo scopriamo assieme!

Python come linguaggio di programmazione, offre una feature abbastanza insolita ma utile: i decorators.

Per fare il precisino e aggiungere un po’ di tecnichese a questo articolo, c’è da specificare che si tratta di metaprogrammazione: in parole semplici è una parte di programma che tenta di modificare un’altra parte del programma in fase di compilazione.

Ciao mi chiamo Lorenzo Neri e sono un informatico: realizzo contenuti per aiutare le persone a padroneggiare l’arte del nuovo millennio, ovvero l’informatica!

Prima di capire nel dettaglio cosa sono e come funzionano i decorator all’interno di Python, è giusto ricordare una cosa.

Qualsiasi cosa in Python è un oggetto.

Sì, qualunque cosa: anche le funzioni.

Le funzioni e i metodi sono “chiamati”… Chiamabili. Sì, italiano portami via.

In inglese diremmo “callable”: in parole dirette e schiette sono “cose che possiamo invocare”.

Se andiamo a vedere nel dettaglio, qualsiasi oggetto che in Python abbia un metodo speciale, noto come “__call__()” è inteso come “callable”.

Muoviamo il nostro primo passo:

Decorator: cosa sono? Sono dei callable che tornano un callable.

Cit. Ma che ca…?!

Cerchiamo di capire un momento la frase di prima.

Decorator: cosa sono? Dei callable che tornano un callable

In termini molto più semplici e comprensibili della frase di prima, un decorator semplicemente è un “qualcosa” che prende una funzione, gli aggiunge delle funzionalità e la ritorna.

Vediamo però un esempio pratico di questo meccanismo:

def aggiungi_funzionalita(funzione):
   def funzione_interna():
      print("vengo 'decorata'")
      funzione()
   return funzione_interna

def normale():
   print("Io sono una funzione normale")

Vediamo anche l’output di ciò che abbiamo finito di scrivere:

>>> normale()
Io sono una funzione normale

>>> decorata = aggiungi_funzionalita(normale)
>>> decorata()
vengo 'decorata'
Io sono una funzione normale

Nell’esempio che abbiamo appena visto, “aggiungi_funzionalita” è un decorator.

Per farla ancora più “italianeggiante”, la funzione “normale” viene “decorata” e la funzione ritornata all’interno della variabile “decorata”.

Sì, il gioco di parole è voluto per rafforzare il concetto.

Decorator: come funzionano?

Il funzionamento nudo e crudo dei decorator l’abbiamo intravisto poco fa.

Un decorator aggiunge nuove funzionalità alla funzione originale: un po’ come impacchettare un regalo, funziona quasi come un wrapper.

Una precisazione va fatta di fronte a ciò: l’oggetto che ottiene la “decorazione” non viene alterato.

In generale, per stringere il funzionamento in una riga, decoriamo una funzione e la riassegnamo a se stessa:

funzione_ordinaria = decoratore(funzione_ordinaria)

Ma fammi un po’ indovinare? Non è questa la sintassi a cui sei abituato vero?

Infatti in Python si utilizza un altra sintassi.

Si utilizza il simbolo “@per indicare un decoratore e per “piazzarlo al punto giusto”.

Ti faccio un esempio al volo:

@impacchetta_la_funzione
def funzione_ordinaria():
   print("Sono una semplice funzione")

È l’equivalente di:

def funzione_ordinaria():
   print("Sono una semplice funzione")
funzione_ordinaria = impacchetta_la_funzione(funzione_ordinaria)

Quindi, fino ad ora dei decorator abbiamo visto sia cosa sono, sia come funzionano.

Ma vediamo un’ultima cosa prima di concludere.

Decorator: come funzionano con i parametri

Se avessimo bisogno di parametrizzare i decorator con i parametri?

Si può fare.

Immagina di avere una funzione parametrica come questa:

def divisione(num1, num2):
   return num1 / num2

Sì, lo so: “Lorenzo ma se si divide per zer…” appunto!

Rendiamo questa funzione un po’ più “smart” 🙂

Aggiungiamo un decorator che impedisca la divisione per zero, o la prevenga insomma:

def divisione_intelligente(funzione_base):
   def interna(num1, num2):
      if num2 == 0:
         print("Non puoi dividere per zero!")
         return
      
      return funzione_base(num1, num2)
   return interna
# usiamo il nostro decorator
@divisione_intelligente
def divisione(num1, num2):
   return num1 / num2

Continua a scoprire di più con questi articoli!

Lascia un commento

Questo sito potrebbe fare uso di cookie e siccome l'UE mi obbliga a fartelo presente, eccoti il classico banner dove puoi decidere come gestirli. Accetta Leggi di più