Qual è la differenza tra una "proprietà" di Python e un "attributo"?


146

Sono generalmente confuso sulla differenza tra una "proprietà" e un "attributo" e non riesco a trovare una grande risorsa per dettagliare concisamente le differenze.

Risposte:


184

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 .


Sembra che le proprietà comportino un'ulteriore elaborazione per accedere al valore target ... sai quanto sia significativo / molto più lento?
martineau,

@martineau: Bene, c'è una chiamata di funzione extra, ma molto probabilmente il lavoro extra e il tempo dipenderanno da quanto la proprietà sta facendo (consiglio generale: fare / non / usare le proprietà per nascondere l'I / O).
Ethan Furman,

56

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

Questo ("controllo completo") può essere fatto anche con attributi "non di proprietà", ma senza tali semplici decoratori.

8
Mi piace che questa risposta fornisca un esempio realistico e utile. Sento che troppe risposte su questo sito spiegano inutilmente come funzionano le cose sul back-end senza chiarire come l'utente dovrebbe interagire con loro. Se non si capisce perché / quando utilizzare alcune funzionalità, non ha senso sapere come funziona dietro le quinte.
Tom,

Questa risposta viola lo "Zen di Python - Dovrebbe esserci uno - e preferibilmente solo uno - un modo ovvio per farlo". Esistono 2 modi per impostare il valore x.
Tin Luu,

@TinLuu - C'è solo un modo per impostare il valore di x. C'è anche un solo modo per impostare il valore di _x. Il fatto che siano la stessa cosa è un dettaglio di implementazione. L'utente saggio di questa classe non usa _x.
acceso il

1
@TinLuu - Penso che entrambi stiamo dicendo la stessa cosa da estremità opposte della prospettiva. L'utente della classe dovrebbe solo vedere x. Senso unico. Se l'utente della classe viene a conoscenza di _x, lo usano a proprio rischio.
acceso il

19

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

1
potresti per favore menzionare anche il risultato atteso da questo codice?
Hasan Iqbal,

2
Cosa intendi? Non è in fondo al codice?
six8

12

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.

http://docs.python.org/library/functions.html#property


Solo una risposta che aveva un senso pratico!
Amico

8

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


8

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.