
Modificare gli attributi di classe in Python
La gestione degli attributi di una classe in Python è essenziale nella programmazione orientata agli oggetti. In questa guida ti spiego come modificare e gestire questi attributi.
Cos'è un attributo di classe? Un attributo di classe è una variabile condivisa tra tutte le istanze di quella classe, definita all'interno della classe stessa ma fuori da qualsiasi metodo. Questi attributi sono ideali per valori costanti o proprietà comuni a tutte le istanze.
Ecco un esempio pratico di attributo di classe
class Car:
doors = 4 # Attributo di classe
Quando crei delle istanze con questa classe 'Car', tutti gli oggetti ereditano l'attributo di classe 'doors'.
myCar1 = Car()
myCar2 = Car()
Ad esempio, se interroghi l'attributo 'doors' dall'istanza myCar2, Python ti restituisce 4.
print(myCar2.doors)
4
Ora vediamo come modificare l'attributo di classe. Ci sono diverse strade per farlo, tramite un metodo diretto oppure con la funzione setattr().
Il metodo diretto
Puoi cambiare un attributo di classe direttamente utilizzando il nome della classe.
Ad esempio, per modificare l'attributo di classe 'doors' della classe 'Car' puoi scrivere:
Car.doors = 5
Python assegna il nuovo valore alla classe.
print(Car.doors)
5
Questo influenzerà tutte le istanze, inclusi gli oggetti esistenti che non hanno sovrascritto l'attributo.
Ad esempio, se ora interroghi l'attributo 'doors' sull'istanza myCar2, il risultato è 5
print(myCar2.doors)
5
Fai attenzione a non modificare l'attributo di classe da un'istanza. In questo caso Python non genera un errore, bensì crea un attributo di istanza nell'oggetto senza modificare l'attributo di classe. Il che è ancora peggio perché potresti non accorgerti del problema. Ad esempio, in questo codice proviamo a modificare l'attributo 'doors' dall'istanza myCar2.
class Car:
doors = 4 # Attributo di classe
myCar1 = Car()
myCar2 = Car()
myCar2.doors=5 # Attributo di istanza
Quando assegni myCar2.doors = 5, crei un nuovo attributo doors solo per l'istanza myCar2. Questo attributo "nasconde" l'attributo di classe originale 'doors' per quella particolare istanza. Quindi, se chiami l'attributo di classe il valore è sempre 4.
print(Car.doors)
4
Lo stesso accade se accedi all'attributo di classe dalle altre istanze. Ad esempio, l'istanza myCar1 non è affatto influenzato da questa modifica e continuerà a mostrare il valore 4 dell'attributo di classe.
print(myCar1.doors)
4
Se invece chiami l'attributo 'doors' dall'istanza myCar2, Python legge l'attributo di istanza dell'oggetto e restituisce 5.
print(myCar2.doors)
5
Il metodo setattr()
La funzione setattr() ti permette di modificare gli attributi di classe in modo dinamico.
Questa funzione è molto utile quando non conosci il nome dell'attributo di classe da modificare, perché dipende dal flusso di elaborazione dello script.
Ad esempio, per modificare l'attributo di classe 'doors' della classe 'Car' puoi scrivere:
setattr(Car, 'doors', 6)
La modifica viene apportata direttamente nella classe.
print(Car.doors)
6
Il nuovo valore dell'attributo di classe viene ereditato automaticamente da tutte le istanze.
Ad esempio, se visualizzi l'attributo 'doors' dall'istanza myCar2, Python ritorna il valore 6.
print(myCar2.doors)
6
Aggiunta di nuovi attributi di classe
Nel linguaggio Python è possibile aggiungere attributi a una classe anche dopo la sua inizializzazione.
Ad esempio, hai già definito una classe 'Car' e generato alcune istanze.
class Car:
doors = 4 # Attributo di classe
myCar1 = Car()
myCar2 = Car()
Puoi aggiungere un altro attributo di classe chiamato 'wheels' semplicemente con un'assegnazione.
Car.wheels = 4
In alternativa, puoi aggiungere un nuovo attributo di classe usando il metodo setattr()
setattr(Car, 'wheels', 6)
In entrambi i casi Python aggiunge un nuovo attributo 'wheels' alla classe 'Car'
print(Car.wheels)
4
Il nuovo attributo viene ereditato automaticamente da tutte le istanze della classe.
print(myCar2.wheels)
4
Ricorda che modificare un attributo di classe influenza tutte le istanze della classe, a meno che non abbiano attributi di istanza con lo stesso nome.
È molto importante comprendere questo comportamento per evitare effetti collaterali indesiderati.
Mantenere una distinzione chiara tra attributi di classe e di istanza può aiutare a prevenire confusione e bug. È importante documentare qualsiasi modifica agli attributi per facilitare la comprensione del codice agli altri sviluppatori.
Il dizionario degli attributi di classe
Ogni classe in Python possiede un dizionario, identificato come __dict__, in cui sono elencati tutti gli attributi relativi alla classe.
Questo dizionario degli attributi di classe è separato e distinto dal dizionario __dict__ appartenente a ciascuna istanza della classe.
I dizionari delle istanze contengono attributi che sono specifici per ciascuna istanza e operano in modo indipendente da quello della classe.
Ad esempio, se hai la classe Car
class Car:
doors = 4 # Attributo di classe
Puoi consultare il dizionaro semplicemente digitando Car.__dict__
Tra i vari attributi c'è anche l'attributo di classe 'doors'
print(Car.__dict__)
{'__module__': '__main__', 'doors': 4, '__dict__': <attribute '__dict__' of 'Car' objects>, '__weakref__': <attribute '__weakref__' of 'Car' objects>, '__doc__': None}
Tuttavia, a differenza del dizionario di un'istanza, in Python il dizionario __dict__ di una classe non è modificabile.
La ragione è che il `__dict__` di una classe in Python non è un semplice dizionario, ma un oggetto di tipo `mappingproxy`, che è una vista di sola lettura del dizionario sottostante degli attributi della classe.
Ad esempio, se provi a modificare l'attributo di classe tramite il `__dict__` di una classe in Python assegnandogli un valore, Python va in errore.
Car.__dict__['doors'] = 5
TypeError: 'mappingproxy' object does not support item assignment
Questo codice solleva un `TypeError` perché stai tentando di sostituire un `mappingproxy` con un dizionario ordinario, il che non è consentito.
Perché Python usa mappingproxy? L'uso di `mappingproxy` è stata una decisione necessaria al rilascio di Python 3 per mantenere l'integrità degli attributi di classe, proteggendoli da modifiche non intenzionali che potrebbero influenzare tutte le istanze di quella classe. Questo aiuta a prevenire bug difficili da tracciare e mantiene il codice più sicuro e manutenibile. In Python 2, invece, il dizionario degli attributi di classe era un normale dizionario ordinario ed era modificabile.
In conclusione, puoi modificare gli attributi di una classe, devi usare approcci che rispettino le regole di Python, come la modifica diretta (ad esempio car.doors=5) o il metodo built-in setattr() ma non puoi farlo tramite il dizionario della classe.
Questi metodi sono supportati perché sono considerati più sicuri, controllati e manutibili.
Con queste conoscenze, ora sai come gestire, modificare e aggiungere gli attributi di classe in Python in modo sicuro ed efficace.