
Il metodo speciale __call__ in Python
Oggi parleremo di un aspetto interessante e potente della programmazione in Python: il metodo speciale __call__.
Cos'è il metodo `__call__`? Questo metodo ti permette di fare qualcosa di un po' insolito ma molto utile: trasforma un'istanza di una classe in un oggetto "chiamabile", come se fosse una funzione.
In Python, tutte le funzioni sono oggetti chiamabili, il che significa che puoi invocarle usando le parentesi tonde `()`.
Ad esempio, definisci una funzione
def saluta():
print("Ciao!")
Poi chiama la funzione.
saluta()
Questo chiama la funzione e stampa
"Ciao!"
Ora, immagina di poter fare la stessa cosa con una istanza, ossia con un oggetto di una classe. Questo è possibile grazie al metodo `__call__`.
Ad esempio, definisci una classe chiamata "Rettangolo".
class Rettangolo:
def __init__(self, base, altezza):
self.base = base
self.altezza = altezza
Poi crea un'istanza della classe e chiamala "rect1".
rect1 = Rettangolo(5,4)
Quest'oggetto memorizza la lunghezza della base (5) e dell'altezza di un rettangolo (4) che gli hai indicato.
print(rect1.base)
5
Tuttavia, non è un oggetto chiamabile perché di default le istanze di una classe non lo sono. Si limitano a memorizzare dei dati.
Quindi, non puoi chiamare l'istanza come se fosse una funzione, passandogli degli argomenti tra parentesi.
print(callable(rect1))
False
Se provi a farlo, Python solleva un'eccezione
print(rect1(2))
Traceback (most recent call last):
File "/home/main.py", line 12, in <module>
print(rect1(2))
TypeError: 'Rettangolo' object is not callable
Per fare in modo che le istanze della classe siano anche chiamabili, devi definire il metodo speciale `__call__`
Quando definisci `__call__` all'interno di una classe, stai dicendo a Python cosa dovrebbe succedere quando un'istanza di quella classe viene "chiamata" come una funzione.
Ad esempio, modifica la classe "Rettangolo" in questo modo:
class Rettangolo:
def __init__(self, base, altezza):
self.base = base
self.altezza = altezza
def area(self):
return self.base * self.altezza
def __call__(self, numero):
return self.area() * numero
Crea un'istanza della classe
rect1 = Rettangolo(5,4)
Ora puoi "chiamare" l'oggetto come se fosse una funzione.
print(callable(rect1))
True
Quando Python rileva le parentesi tonde dopo l'istanza, esegue automaticamente il metodo speciale __call__
print(rect1(2))
In questo caso il metodo speciale __call__ calcola l'area del rettangolo e la moltiplica per il numero passato come argomento, ossia per 2.
Il risultato è 4x5x2=40
40
In conclusione, il metodo `__call__` in Python permette alle istanze di comportarsi come funzioni pur mantenendo tutti i vantaggi di un oggetto, come lo stato interno e i metodi associati.
Perché usare `__call__`? L'uso del metodo `__call__` può essere molto utile in vari scenari. Ad esempio, puoi creare oggetti che si comportano come funzioni personalizzate ma che conservano uno stato o altre informazioni utili. È uno strumento particolarmente utile quando vuoi combinare la semplicità di una funzione con la complessità di un oggetto.
Quindi, la prossima volta che crei una classe e pensi che possa essere usata come una funzione, considera l'aggiunta di `__call__` per renderla ancora più intuitiva e versatile.