2009-02-28 7 views
135

Donc, je joue avec des décorateurs en Python 2.6, et j'ai du mal à les faire fonctionner. Voici mon fichier de classe:Pourquoi @ foo.setter en Python ne fonctionne pas pour moi?

class testDec: 

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

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

Ce que je pensais que cela voulait dire est de traiter x comme une propriété, mais appeler ces fonctions sur get et set. Alors, je pète IDLE et vérifié:

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

Il est clair que le premier appel fonctionne comme prévu, puisque j'appelle le getter, et il n'y a pas de valeur par défaut, et il tombe en panne. OK, bien, je comprends. Cependant, l'appel à attribuer t.x = 5 semble créer une nouvelle propriété x, et maintenant le getter ne fonctionne pas!

Qu'est-ce qui me manque?

+6

S'il vous plaît Nommez Classes avec une lettre majuscule. vous pouvez utiliser des minuscules pour les variables et les fichiers de module. –

Répondre

258

Vous semblez être en utilisant classic old-style classes. Pour que properties fonctionne correctement, vous devez utiliser new-style classes à la place (inherit from object). Il suffit de déclarer votre classe comme 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 

Il fonctionne:

>>> 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, il n'est plus nécessaire - les classes "classiques" sont juste correctes avec les setters :-) – Eenoku

+14

Cela fonctionne en python 3 car chaque classe est une nouvelle classe, il n'y a plus de classes 'classiques'. – craigds

22

Vous devez utiliser des classes nouveau style que vous faites en dérivant votre classe de l'objet:

class testDec(object): 
    .... 

Ensuite, il devrait fonctionner.

61

Juste une note pour d'autres personnes qui tombent par hasard ici en quête de cette exception: les deux fonctions doivent avoir le même nom. Nommer les méthodes comme suit entraînera une exception:

@property 
def x(self): pass 

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

les deux méthodes donnent lieu le même nom

@property 
def x(self): pass 

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

Il est également important de noter que l'ordre des questions de déclaration. Le getter doit être définie avant le setter dans le fichier ou bien vous obtiendrez un NameError: name 'x' is not defined

+0

Cela va probablement devenir la "nouvelle" meilleure réponse, avec de plus en plus de gens sur Python 3 ... – trpt4him

+3

Cette réponse est géniale. Je pense que pour être complet, ce serait bien si vous pouvez remarquer que l'ordre est important, c'est-à-dire que le getter doit être devant le setter dans le code. Sinon, vous obtiendrez une exception 'NameError: name 'x' is not defined'. – eguaio

+0

Merci pour ça. Je viens de gâcher la meilleure partie d'une journée là-dessus. Il ne m'est jamais venu à l'esprit que les méthodes devaient être appelées la même chose. Vraiment frustrant petit idiome! – ajkavanagh

8

Dans le cas où tout le monde vient ici de Google, en plus des réponses ci-dessus, je voudrais ajouter que cela a besoin d'attention lors de l'appel du setter de la méthode __init__ de votre classe basée sur this answer Plus précisément:

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