
Il metodo __getattr__() in Python
In Python `__getattr__()` è un metodo speciale che viene invocato automaticamente quando si tenta di accedere a un attributo che non è stato definito.
oggetto.__getattr__()
Questo metodo serve a gestire l'accesso ad attributi che non esistono in un oggetto.
Ti permette di intercettare queste richieste e fornire un valore predefinito o un comportamento alternativo.
In altre parole, se cerchi di ottenere qualcosa che non è presente direttamente nell'oggetto, Python ti dice "Aspetta un attimo! Prima di fallire, vediamo se c'è un modo alternativo per gestire questa richiesta."
Ti faccio un esempio.
Crea una classe chiamata 'Persona'.
class Persona:
def __init__(self, nome):
self.nome = nome
Poi genera un'istanza della classe.
p = Persona("Luca")
Se provi ad accedere all'attributo 'nome' dell'oggetto, questo restituisce il valore che gli hai assegnato.
print(p.nome)
Luca
Ora prova ad accedere a un attributo inesistente nell'oggetto, ad esempio 'p.eta'
print(p.eta)
Python non trova l'attributo 'eta' nell'oggetto e restituisce un errore.
Traceback (most recent call last):
File "/home/main.py", line 8, in <module>
print(p.eta)
AttributeError: 'Persona' object has no attribute 'eta'
Per evitare questo problema puoi definire il metodo '__getattr__()' nella classe 'Persona'.
Questo metodo viene chiamato automaticamente quando tenti di accedere a un attributo inesistente.
class Persona:
def __init__(self, nome):
self.nome = nome
def __getattr__(self, name):
return f"L'attributo '{name}' non è presente."
Genera un'istanza della classe. Ad esempio, l'oggetto 'p'.
p = Persona("Luca")
Ora prova ad accedere a un attributo inesistente dell'oggetto 'p'.
print(p.eta)
Python non trova l'attributo 'eta' nell'oggetto e chiama automaticamente il metodo '__getattr__()' che stampa a video un messaggio.
L'attributo non è presente
In questo modo, anche se un attributo non esiste, il programma restituisce qualcosa di sensato anziché un errore.
Differenza tra `__getattr__()` e `__getattribute__()`
In Python esiste anche un altro metodo simile chiamato `__getattribute__()`. Fai attenzione a non confonderti!
- Il metodo `__getattr__()` viene chiamato solo se l'attributo non esiste.
- Il metodo `__getattribute__()`, invece, viene chiamato ogni volta che cerchi di accedere a un attributo, indipendentemente dal fatto che esista o meno.
Di solito, per la maggior parte dei casi, `__getattr__()` è più che sufficiente. `__getattribute__()` è un po' più pericoloso, perché può interferire con l'accesso normale agli attributi, quindi va usato con cautela.
Lazy loading
Il metodo '__getattr__()' può essere utilizzato per caricare gli attributi su richiesta, utile per ridurre il carico iniziale o per accedere a risorse che richiedono un'elaborazione lenta.
Ecco un esempio pratico di lazy loading:
class Persona:
def __init__(self, nome):
self.nome = nome
self._eta = None # L'attributo eta è inizialmente non definito
def __getattr__(self, name):
if name == 'eta':
if self._eta == None:
print(f"L'attributo '{name}' non è stato caricato. Inserisci la tua età:")
# Chiediamo all'utente di inserire l'età
var = int(input("Inserisci la tua età: "))
super().__setattr__('_eta', var)
return self._eta
In questa classe l'età della persona non viene richiesta per inizializzare un oggetto.
Se l'utente accede all'attributo 'eta' e questo non è ancora stato definito, viene richiesto di inserire l'età tramite input (input()).
Crea un'istanza della classe.
p = Persona("Anna")
Ora cerca di accedere all'attributo 'eta' dell'oggetto
print(p.eta)
Poiché non è ancora definito, Python ti chiede di digitare l'età della persona.
L'attributo 'eta' non è stato caricato. Inserisci la tua età:
Inserisci la tua età:
In conclusione, il metodo `__getattr__()` è come un meccanismo di fallback che ti permette di gestire elegantemente casi in cui gli attributi non esistono.
Usato con saggezza, può semplificare il tuo codice, evitando un sacco di `if` e verifiche di esistenza.