Storia della questione:
Nella OOP classica, l'incapsulamento è realizzato attraverso campi privati e getter/setter, il che è ingombrante e non "pythonico". In Python, a partire dalla versione 2.2, è stato introdotto il decoratore @property, che permette di accedere ai metodi getter e setter come normali attributi, realizzando un'incapsulazione efficace con una sintassi comoda.
Problema:
Senza i decoratori property è necessario definire esplicitamente i metodi per accedere e impostare valori (ad esempio, get_x() e set_x(val)), il che rende il codice meno leggibile e gli utenti della classe non sono protetti dall'accesso diretto ai dati interni. Si presentano problemi durante il refactoring e la modifica della logica interna di memorizzazione o calcolo dei valori.
Soluzione:
Il decoratore @property consente di definire getter, setter e deleter con una singola sintassi. Questo risulta conciso, comodo, incapsula i dettagli di implementazione e consente di cambiare il modo di calcolare una proprietà senza interrompere l'interfaccia della classe.
Esempio di codice:
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("Temperature below -273.15°C is not possible!") self._celsius = value
Caratteristiche chiave:
@property consente di accedere ai metodi come se fossero normali attributi.Si può creare una proprietà solo in lettura, ma non in scrittura/eliminazione?
Sì, se si definisce solo il metodo con il decoratore @property senza setter e deleter, la proprietà sarà accessibile solo in lettura.
class Sample: @property def value(self): return 42
Cosa succede se il nome della property coincide con un attributo privato?
Di solito, la property è utilizzata come "intermediario" per un attributo privato, il cui nome inizia con un trattino basso (ad esempio, _x). Occorre evitare tale coincidenza, altrimenti si avrà una chiamata ricorsiva:
class Bad: @property def x(self): return self.x # Ricorsione infinita
Si può assegnare una property solo per la classe?
No, il decoratore standard @property funziona con le istanze della classe. Per creare una property di classe, si devono utilizzare pattern di terze parti o librerie speciali (il @classmethod insieme a property non funziona direttamente).
Un sviluppatore accede direttamente al campo della classe (self.celsius), invece di utilizzare la property. Successivamente viene aggiunta la validazione, ma i consumatori della classe possono comunque modificare direttamente l'attributo privato e bypassare i controlli.
Pro:
Facile e veloce lavorare, finché non ci sono logiche complesse.
Contro:
L'incapsulamento è compromesso, può essere facile ottenere uno stato errato dell'oggetto, si crea confusione.
Utilizzare property e nascondere l'attributo interno tramite _celsius. La validazione, il caching e la logica sono centralizzati all'interno della property.
Pro:
Il codice è protetto, l'interfaccia è stabile: è possibile cambiare l'implementazione della proprietà senza influenzare gli utenti della classe.
Contro:
Con oggetti grandi e complessi, si può aumentare la difficoltà di debug, se si abusa di property.