Risposte:
Le proprietà sono un tipo speciale di attributo. Fondamentalmente, quando Python incontra il seguente codice:
spam = SomeObject()
print(spam.eggs)
si guarda in alto eggsin spam, e poi prende in esame eggsper vedere se ha una __get__, __set__o __delete__metodo - se lo fa, si tratta di una proprietà. Se è una proprietà, invece di restituire l' eggsoggetto (come farebbe per qualsiasi altro attributo) chiamerà il __get__metodo (dal momento che stavamo facendo la ricerca) e restituirà qualunque cosa restituisca quel metodo.
Ulteriori informazioni sul modello di dati e sui descrittori di Python .
Con una proprietà hai il controllo completo sui suoi metodi getter, setter e deleter, che non hai (se non usi avvertimenti) con un attributo.
class A(object):
_x = 0
'''A._x is an attribute'''
@property
def x(self):
'''
A.x is a property
This is the getter method
'''
return self._x
@x.setter
def x(self, value):
"""
This is the setter method
where I can check it's not assigned a value < 0
"""
if value < 0:
raise ValueError("Must be >= 0")
self._x = value
>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
File "ex.py", line 15, in <module>
a.x = -1
File "ex.py", line 9, in x
raise ValueError("Must be >= 0")
ValueError: Must be >= 0
x. Senso unico. Se l'utente della classe viene a conoscenza di _x, lo usano a proprio rischio.
In termini generali, una proprietà e un attributo sono la stessa cosa. Tuttavia, esiste un decoratore di proprietà in Python che fornisce l'accesso getter / setter a un attributo (o altri dati).
class MyObject(object):
# This is a normal attribute
foo = 1
@property
def bar(self):
return self.foo
@bar.setter
def bar(self, value):
self.foo = value
obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
La proprietà ti permette di ottenere e impostare valori come faresti con gli attributi normali, ma sotto c'è un metodo che viene chiamato traducendolo in un getter e setter per te. È davvero solo una comodità ridurre la piastra di chiamata di getter e setter.
Diciamo ad esempio che avevi una classe che conteneva alcune coordinate xey per qualcosa di cui avevi bisogno. Per impostarli potresti voler fare qualcosa del tipo:
myObj.x = 5
myObj.y = 10
È molto più facile da guardare e pensare che scrivere:
myObj.setX(5)
myObj.setY(10)
Il problema è che cosa succede se un giorno la tua classe cambia in modo tale da dover compensare la tua xey da un valore? Ora avresti bisogno di entrare e cambiare la definizione della tua classe e tutto il codice che lo chiama, che potrebbe richiedere molto tempo ed essere soggetto ad errori. La proprietà consente di utilizzare la prima sintassi offrendo la flessibilità di cambiare quest'ultima.
In Python, puoi definire getter, setter ed eliminare metodi con la funzione proprietà. Se vuoi solo la proprietà read, c'è anche un decoratore @property che puoi aggiungere sopra il tuo metodo.
Ho imparato 2 differenze dal sito di Bernd Klein, in sintesi:
1. La proprietà è un modo più conveniente per eseguire l'incapsulamento dei dati.
es: se hai una lunghezza di attributo pubblica di Object, in seguito, il tuo progetto ti richiede di incapsularlo, cioè: cambiarlo in privato e fornire getter e setter => devi cambiare molti dei codici che hai scritto prima:
#Old codes
obj1.length=obj1.length+obj2.length
#New codes(Using private attibutes and getter and setter)
obj1.set_lenght(obj1.get_length()+obj2.get_length()) #=> this is ugly
Se usi @property e @ lenght.setter => non devi cambiare quei vecchi codici
2. Una proprietà può incapsulare più attributi
class Person:
def __init__(self, name, physic_health, mental_health):
self.name=name
self.__physic_health=physic_health #physic_health is real value in range [0, 5.0]
self.__mental_health=mental_health #mental_health is real value in range [0, 5.0]
@property
def condition(self):
health=self.__physic_health+self.__mental_health
if(health<5.0):
return "I feel bad!"
elif health<8.0:
return "I am ok!"
else:
return "Great!"
In questo esempio, __physic_healthe __mental_healthsono privati e non è possibile accedervi direttamente dall'esterno, l'unico modo in cui la classe esterna interagisce con loro è attraverso la proprietàcondition
C'è anche una differenza non ovvia che uso per memorizzare nella cache o aggiornare i dati, spesso abbiamo una funzione collegata all'attributo di classe. Ad esempio, devo leggere il file una volta e mantenere il contenuto assegnato all'attributo in modo che il valore venga memorizzato nella cache:
class Misc():
def __init__(self):
self.test = self.test_func()
def test_func(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Produzione:
func running
func value
func value
Abbiamo avuto accesso all'attributo due volte ma la nostra funzione è stata attivata una sola volta. La modifica dell'esempio precedente per utilizzare la proprietà comporterà l'aggiornamento del valore dell'attributo ogni volta che si accede ad esso:
class Misc():
@property
def test(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Produzione:
func running
func value
func running
func value