Decoratori Python: Aggiungere Superpoteri alle Tue Funzioni

Ciao a tutti, aspiranti programmatori Python! Oggi, esploreremo il fascinante mondo dei decoratori Python. Immagina i decoratori come wrappatori magici che possono arricchire le tue funzioni con superpoteri. Entusiastico, vero? Iniziamo insieme questo viaggio!

Python - Decorators

Cos'è un Decoratore?

Immagina di avere un regalo bellissimamente avvolto. La carta da regalo non cambia il regalo stesso, ma lo rende più bello, giusto? Esattamente quello che fanno i decoratori alle tue funzioni in Python. Wrappano le tue funzioni, aggiungendo funzionalità extra senza cambiare la funzione originale.

Iniziamo con un esempio semplice:

def mio_decoratore(func):
def wrapper():
print("Qualcosa sta accadendo prima che la funzione venga chiamata.")
func()
print("Qualcosa sta accadendo dopo che la funzione è stata chiamata.")
return wrapper

@mio_decoratore
def dice_ciao():
print("Ciao!")

dice_ciao()

Se esegui questo codice, vedrai:

Qualcosa sta accadendo prima che la funzione venga chiamata.
Ciao!
Qualcosa sta accadendo dopo che la funzione è stata chiamata.

Smontiamo il codice:

  1. Definiamo una funzione decoratore mio_decoratore che prende una funzione come argomento.
  2. All'interno di mio_decoratore, definiamo una funzione wrapper che aggiunge alcuni comportamenti prima e dopo la chiamata della funzione originale.
  3. Utilizziamo la sintassi @mio_decoratore per applicare il nostro decoratore alla funzione dice_ciao.
  4. Quando chiamiamo dice_ciao(), in realtà stiamo chiamando la versione wrappata della funzione.

Non è carino? Abbiamo appena aggiunto qualche comportamento extra alla nostra funzione dice_ciao senza modificare il suo codice!

Decoratori con Argomenti

Ma aspetta, c'è di più! Cosa facciamo se la nostra funzione prende argomenti? Nessun problema! Possiamo modificare il nostro decoratore per gestirli:

def mio_decoratore(func):
def wrapper(*args, **kwargs):
print("Prima che la funzione venga chiamata.")
risultato = func(*args, **kwargs)
print("Dopo che la funzione è stata chiamata.")
return risultato
return wrapper

@mio_decoratore
def aggiungi(a, b):
return a + b

print(aggiungi(3, 5))

Questo produrrà l'output:

Prima che la funzione venga chiamata.
Dopo che la funzione è stata chiamata.
8

Qui, *args e **kwargs permettono al nostro decoratore di lavorare con qualsiasi numero di argomenti posizionali e nomeati.

Decoratori Integrati

Python offre alcuni decoratori integrati incredibilmente utili. Esploriamoli!

Il Decoratore @classmethod

Il decoratore @classmethod viene utilizzato per definire metodi che operano sulla classe stessa, piuttosto che su istanze della classe.

class Pizza:
def __init__(self, ingredienti):
self.ingredienti = ingredienti

@classmethod
def margherita(cls):
return cls(['mozzarella', 'pomodori'])

@classmethod
def prosciutto(cls):
return cls(['mozzarella', 'pomodori', 'prosciutto'])

print(Pizza.margherita().ingredienti)
print(Pizza.prosciutto().ingredienti)

Questo produrrà l'output:

['mozzarella', 'pomodori']
['mozzarella', 'pomodori', 'prosciutto']

Qui, margherita e prosciutto sono metodi di classe che creano e restituiscono nuove istanze di Pizza con ingredienti predefiniti.

Il Decoratore @staticmethod

I metodi statici sono metodi che non operano su istanze o classi. Sono semplicemente funzioni regolari che si trovano all'interno di una classe.

class Matematica:
@staticmethod
def aggiungi(a, b):
return a + b

print(Matematica.aggiungi(5, 10))

Questo produrrà l'output:

15

Il Decoratore @property

Il decoratore @property ti permette di definire metodi che puoi accedere come attributi.

class Cerchio:
def __init__(self, raggio):
self._raggio = raggio

@property
def raggio(self):
return self._raggio

@property
def area(self):
return 3.14 * self._raggio ** 2

c = Cerchio(5)
print(c.raggio)
print(c.area)

Questo produrrà l'output:

5
78.5

Qui, possiamo accedere a raggio e area come se fossero attributi, ma in realtà sono metodi.

Il Decoratore @functools.wraps

Questo decoratore viene utilizzato per preservare i metadati della funzione originale quando si creano decoratori.

from functools import wraps

def mio_decoratore(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""Questa è la funzione wrapper"""
return func(*args, **kwargs)
return wrapper

@mio_decoratore
def mia_funzione():
"""Questa è la mia funzione"""
pass

print(mia_funzione.__name__)
print(mia_funzione.__doc__)

Questo produrrà l'output:

mia_funzione
Questa è la mia funzione

Senza @wraps, il nome della funzione e la docstring sarebbero quelli della funzione wrapper.

Conclusione

I decoratori sono una caratteristica potente di Python che ti permette di modificare o arricchire funzioni e metodi. Sono ampiamente utilizzati nei framework e nelle librerie per aggiungere funzionalità come il logging, il controllo degli accessi e altro.

Ricorda, la chiave per padroneggiare i decoratori è la pratica. Prova a creare i tuoi decoratori e a applicarli a diverse funzioni. Presto, sarai in grado di wrappare le tue funzioni con superpoteri come un professionista!

Ecco una tabella che riassume i decoratori che abbiamo coperto:

Decoratore Scopo
@classmethod Definisci metodi che operano sulla classe stessa
@staticmethod Definisci metodi che non operano su istanze o classi
@property Definisci metodi che possono essere accesi come attributi
@functools.wraps Preserva i metadati della funzione originale nei decoratori

Buon coding, e che le tue funzioni siano sempre bellissimamente decorate!

Credits: Image by storyset