Proprietà
Proprietà vs Attributi
Le proprietà sono modi per ottenere e impostare il valore di alcuni attributi (ma non solo, possono esserci proprietà calcolate) senza dover richiamare le funzioni getter e setter, come abbiamo visto negli esempi fino a qui. Per i programmatori Python, questa è una tecnica usata molto comunemente, considerata l'estrema semplicità di implementazione.
class Fruit(object):
.....
@property
def quality(self):
return self.__quality
@quality.setter
def quality(self, value):
if 0 <= value <= 100:
self.__quality = value
else:
self.__quality = 50
self.__addAlert('bad quality: %d' % value)
def main():
myapple = Fruit('mela granny smith')
myapple.quality = 10
print('quality %d' % myapple.quality)
print('Allarmi: ', *myapple.getAlerts())
myapple.quality = -20
print('quality %d' % myapple.quality)
print('Allarmi: ', *myapple.getAlerts())
L'output è come il seguente:
quality 10
Allarmi:
quality 50
Allarmi: bad quality: -20
Come si vede, nonostante l'uso sia simile a quello degli attributi pubblici, in realtà vengono richiamate le funzioni specifiche impostate.
Uso dell'attributo di classe __slots__
Come abbiamo già visto, si può usare l'attributo di classe __slots__
per indicare una lista di attributi di istanza validi. È sufficiente inserire nel codice la riga
__slots__ = ('name', '__alerts', '__quality')
per far sì che il codice seguente, altrimenti valido, provochi un errore:
myapple = Fruit('mela granny smith')
myapple.quality = 10
myapple.color='verde'
print(myapple.color)
>>>
Traceback (most recent call last):
File ".../Fruit.py", line 58, in <module>
main()
File "/.../Fruit.py", line 54, in main
myapple.color='verde'
AttributeError: 'Fruit' object has no attribute 'color'
Proprietà calcolate
Alcune proprietà possono essere calcolate a partire dal valore degli attributi. Per esse si può impostare solo il getter, e non il setter. Ad esempio, si consideri il seguente codice:
@property
def is_good(self):
return self.quality > 70
Sarà possibile eseguire queste istruzioni:
myapple = Fruit('mela granny smith')
myapple.quality = 10
print(myapple.quality, myapple.is_good)
myapple.quality = 75
print(myapple.quality, myapple.is_good)
ma non questa:
myapple.is_good = False
# non funziona (manca il setter)
Eliminazione di un attributo
Gli attributi possono essere eliminati. Per farlo, è sufficiente usare la parola chiave del:
>>> f=Foo() # immaginiamo di avere una classe Foo.
>>> f.bar=1
>>> print(f.bar)
1
>>> del f.bar
>>> print(f.bar)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
print(f.bar)
AttributeError: 'Foo' object has no attribute 'bar'
Se usiamo le proprietà, possiamo definire una funzione che verrà invocata in occasione della cancellazione:
@quality.deleter
def quality(self):
del self.__quality
Visto che consentiamo la cancellazione, sarà necessario gestire anche il getter in maniera più articolata, ad esempio con un try... except:
@property
def quality(self):
'''Quality of the picture. Can be any value between 0 and 100 (included).'''
try:
return self.__quality
except AttributeError as err:
return None
Già che ci siamo, possiamo aggiungere una stringa di documentazione sulla proprietà, da poter richiamare in caso di necessità:
print(Fruit.quality.__doc__)
Quality of the picture. Can be any value between 0 and 100 (included).
Proprietà impostate senza uso di decoratori
Una proprietà può essere impostata senza uso di decoratori, tramite la funzione predefinita property
, che permette di indicare, nell'ordine, getter, setter, deleter e docstring:
def getQuality(self):
try:
return self.__quality
except AttributeError as err:
return None
def setQuality(self, value):
if 0 <= value <= 100:
self.__quality = value
else:
self.__quality = 50
self.__addAlert('bad quality: %d' % value)
def delQuality(self):
print('removing quality')
del self.__quality
quality=property(
getQuality,
setQuality,
delQuality,
'Quality of the picture. Can be any value between 0 and 100 (included).'
)