2009-02-28 21 views
135

Por lo tanto, estoy jugando con decoradores en Python 2.6 y estoy teniendo problemas para hacer que funcionen. Aquí está mi archivo de clase:¿Por qué @ foo.setter en Python no funciona para mí?

class testDec: 

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

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

Lo que pensé que esto significaba es tratar x como una propiedad, pero llamar a estas funciones en obtener y definir. Por lo tanto, me dispararon hasta ralentí y comprobado: trabaja

>>> 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 

Es evidente que la primera llamada como se esperaba, ya que llamo el comprador, y no hay ningún valor por defecto, y se produce un error. OK, bien, lo entiendo. Sin embargo, la llamada para asignar t.x = 5 parece crear una nueva propiedad x, ¡y ahora el captador no funciona!

¿Qué me estoy perdiendo?

+6

favor nombre clases con una letra mayúscula. puede usar minúsculas para variables y archivos de módulo. –

Respuesta

258

Parece que está usando classic old-style classes. Para que properties funcione correctamente, necesita usar new-style classes en su lugar (inherit from object). Sólo declarar una clase como 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 

Funciona:

>>> 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 
>>> 
+2

En Python3 ya no es necesario - las clases "clásicas" están bien con setters :-) – Eenoku

+14

Funciona en python 3 porque cada clase es de estilo nuevo, ya no hay clases "clásicas". – craigds

22

Es necesario utilizar las clases de nuevo estilo la que se hace mediante la derivación de la clase de objeto:

class testDec(object): 
    .... 

entonces debería funcionar.

61

Sólo una nota para otras personas que tropiezan aquí en busca de esta excepción: ambas funciones deben tener el mismo nombre. Nombrar los métodos de la siguiente manera dará lugar a una excepción:

@property 
def x(self): pass 

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

En vez dan ambos métodos el mismo nombre

@property 
def x(self): pass 

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

También es importante tener en cuenta que el orden de los asuntos de declaración. El captador debe definirse antes que el colocador en el archivo o de lo contrario obtendrás un NameError: name 'x' is not defined

+0

Esto probablemente se convierta en la "nueva" mejor respuesta, con más y más personas en Python 3 ... – trpt4him

+3

Esta respuesta es genial. Creo que para estar completo, sería genial si puede comentar que el orden es importante, es decir, que el captador debe estar antes que el colocador en el código. De lo contrario, obtendría una excepción 'NameError: name 'x' is not defined'. – eguaio

+0

Gracias por esto. Solo desperdicié la mejor parte de un día en esto. Nunca se me ocurrió que los métodos debían llamarse lo mismo. Muy poco lenguaje frustrante! – ajkavanagh

8

En caso de que alguien venga aquí desde google, además de las respuestas anteriores, me gustaría añadir que esto requiere una atención cuidadosa al invocar al colocador a partir del método __init__ de la clase basado en this answer Específicamente:

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 
Cuestiones relacionadas