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).'
)