
Il metodo __getattribute__() in Python
In Python, quando accedi a un attributo di un oggetto, il linguaggio non va semplicemente a prendere l'attributo direttamente. No, prima passa per __getattribute__(), che è responsabile di cercare quell'attributo.
oggetto.__getattribute__()
Se l'attributo esiste, __getattribute__() lo restituisce, altrimenti solleva un'eccezione AttributeError.
Quindi, ogni volta che cerchi un attributo in un oggetto, tutto passa attraverso questo metodo, sempre.
Qual è la differenza tra __getattribute__() e __getattr__()? Il metodo __getattribute__() viene chiamato sempre, ogni volta che si tenta di accedere a un attributo di un oggetto. Il metodo __getattr__(), invece, viene chiamato solo quando l'attributo non esiste all'interno dell'oggetto.
Ecco come funziona.
Supponiamo che tu abbia questa semplice classe:
class Studente:
def __init__(self, nome, età):
self.nome = nome
self.età = età
Crea un'istanza della classe.
s = Studente('Anna', 22)
Ora prova ad accedere all'attributo 'nome' dell'oggetto.
print(s.nome)
Quando invii quessta richiesta, Python dietro le quinte chiama il metodo s.__getattribute__('nome') per recuperare il valore 'Anna'.
Anna
Quindi, potresti ottenere lo stesso risultato anche usando direttamente il metodo __getattribute__()
print(s.__getattribute__('nome'))
Anna
Fin qui dovrebbe essere tutto chiaro, ma potrebbe anche sembrarti inutile.
Perché dovresti usare __getattribute__()?
In realtà, il vero vantaggio del metodo __getattribute__() è che ti permette di personalizzare le operazioni di ricerca degli attributi negli oggetti.
In altre parole, puoi sovrascrivere __getattribute__() per personalizzare il comportamento della classe quando qualcuno accede agli attributi dei suoi oggetti.
Ecco un esempio pratico.
class Studente:
def __init__(self, nome, età):
self.nome = nome
self.età = età
def __getattribute__(self, attr):
print(f"Sto cercando l'attributo {attr}")
return super().__getattribute__(attr)
Questo semplice codice visualizza un messaggio personalizzato ogni volta che qualcuno accede a un attributo di una classe.
Ad esempio, genera un'istanza della classe
s = Studente('Anna', 22)
Poi prova ad accedere a un attributo:
print(s.nome)
Vedi? Prima di restituire il valore di nome, Python stampa quel messaggio. Questo accade perché hai modificato __getattribute__().
Sto cercando l'attributo nome
Anna
Questo esempio è banale ma puoi modificarlo per fare operazioni più complesse, come proteggere l'accesso a certi attributi o aggiungere logica particolare prima di restituire valori.
Attenzione: evita i cicli infiniti. Se all’interno di __getattribute__() provi ad accedere direttamente a un attributo come self.nome, boom, hai appena creato un ciclo infinito! Questo perché chiameresti di nuovo __getattribute__() per accedere a quell’attributo, e così via, all’infinito. La soluzione? Usa sempre super().__getattribute__(attr) per evitare il ciclo e chiamare il metodo originale.
Quando usare __getattribute__()?
Devi pensare a __getattribute__() come a una guardia che controlla ogni accesso agli attributi di un oggetto, ma richiede un uso consapevole e accurato per evitare problemi.
Di solito, è più comune usare __getattr__(), che viene chiamato solo quando l'attributo non esiste.
Tuttavia, se vuoi avere controllo totale su ogni singolo accesso agli attributi, __getattribute__() è lo strumento giusto, perché viene chiamato sempre, indipendentemente dal fatto che l'attributo esista o meno.
Ma attenzione! Il metodo __getattribute__() è così fondamentale che se lo modifichi, rischi di incasinare tutto. Se non gestisci bene la situazione, potresti creare un ciclo infinito, perché qualsiasi accesso a un attributo, anche quelli interni, passa per __getattribute__().
In sintesi, __getattribute__() è uno strumento molto utile ma va usato con cautela.