Perché @ foo.setter in Python non funziona per me?


155

Quindi, sto giocando con i decoratori in Python 2.6 e ho qualche problema a farli funzionare. Ecco il mio file di classe:

class testDec:

    @property
    def x(self): 
        print 'called getter'
        return self._x

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value

Ciò che pensavo significasse è trattare xcome una proprietà, ma chiama queste funzioni su get e set. Quindi, ho acceso IDLE e controllato:

>>> from testDec import testDec
from testDec import testDec
>>> t = testDec()
t = testDec()
>>> t.x
t.x
called getter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "testDec.py", line 18, in x
    return self._x
AttributeError: testDec instance has no attribute '_x'
>>> t.x = 5
t.x = 5
>>> t.x
t.x
5

Chiaramente la prima chiamata funziona come previsto, poiché chiamo il getter, e non esiste un valore predefinito e non riesce. OK, bene, ho capito. Tuttavia, la chiamata da assegnare t.x = 5sembra creare una nuova proprietà xe ora il getter non funziona!

Cosa mi sto perdendo?


6
Si prega di nominare le classi con una lettera maiuscola. puoi usare lettere minuscole per variabili e file di moduli.
S. Lott,

Risposte:


306

Sembra che tu stia usando le classiche classi vecchio stile in Python 2. Per far funzionare correttamente le proprietà devi invece usare le classi nuovo stile (in Python 2 devi ereditare daobject ). Dichiara semplicemente la tua classe come MyClass(object):

class testDec(object):

    @property
    def x(self): 
        print 'called getter'
        return self._x

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value

Funziona:

>>> k = testDec()
>>> k.x
called getter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/devel/class_test.py", line 6, in x
    return self._x
AttributeError: 'testDec' object has no attribute '_x'
>>> k.x = 5
called setter
>>> k.x
called getter
5
>>> 

Un altro dettaglio che potrebbe causare problemi è che entrambi i metodi richiedono lo stesso nome per il funzionamento della proprietà. Se si definisce il setter con un nome diverso come questo, non funzionerà :

@x.setter
def x_setter(self, value):
    ...

E un'altra cosa che non è completamente facile da individuare all'inizio, è l'ordine: il getter deve essere definito per primo . Se si definisce prima il setter, si ottiene un name 'x' is not definederrore.


4
In Python3 non è più necessario: le classi "classiche" vanno bene con i setter :-)
Eenoku,

20
Funziona in Python 3 perché ogni classe è una nuova classe, non ci sono più classi "classiche".
Craigds,

Penso che si dovrebbe ricevere un avviso / errore se il decoratore non viene elaborato correttamente in questo caso.
stfn

82

Solo una nota per le altre persone che inciampano qui alla ricerca di questa eccezione: entrambe le funzioni devono avere lo stesso nome. La denominazione dei metodi come segue comporterà un'eccezione:

@property
def x(self): pass

@x.setter
def x_setter(self, value): pass

Assegna invece a entrambi i metodi lo stesso nome

@property
def x(self): pass

@x.setter
def x(self, value): pass

È anche importante notare che l'ordine della dichiarazione è importante. Il getter deve essere definito prima del setter nel file altrimenti otterrai unNameError: name 'x' is not defined


Questa probabilmente diventerà la "nuova" migliore risposta, con sempre più persone su Python 3 ...
trpt4him

5
Questa risposta è fantastica Penso che per essere completo, sarebbe bello se si può notare che l'ordine è importante, cioè il getter deve essere prima del setter nel codice. Altrimenti, otterresti NameError: name 'x' is not definedun'eccezione.
eguaio,

Grazie per questo. Ho appena perso la parte migliore di una giornata in questo. Non mi è mai venuto in mente che i metodi dovevano essere chiamati la stessa cosa. Piccolo idioma davvero frustrante!
ajkavanagh,

Per capire il "perché" dietro questa risposta, leggi la documentazione definitiva qui: docs.python.org/2/library/functions.html#property Nota in particolare la parte "esattamente equivalente".
Evgeni Sergeev,

23

Devi usare le nuove classi di stile che fai derivando la tua classe dall'oggetto:

class testDec(object):
   ....

Quindi dovrebbe funzionare.


Tutto sopra era a posto. Questo era necessario per me in python 2.7.x affinché le proprietà nelle classi potessero esplodere correttamente.
Drone Brain,

12

Nel caso in cui qualcuno venga qui da Google, oltre alle risposte di cui sopra, vorrei aggiungere che ciò richiede un'attenta attenzione quando si richiama il setter dal __init__metodo della propria classe basato su questa risposta. In particolare:

class testDec(object):                                                                                                                                            

    def __init__(self, value):
        print 'We are in __init__'
        self.x = value # Will call the setter. Note just x here
        #self._x = value # Will not call the setter

    @property
    def x(self):
        print 'called getter'
        return self._x # Note the _x here

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value # Note the _x here

t = testDec(17)
print t.x 

Output:
We are in __init__
called setter
called getter
17

Non solo dal __init__metodo, ma da qualsiasi altro metodo nella classe.
Mia
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.