Warum funktioniert @ foo.setter in Python bei mir nicht?

155

Ich spiele also mit Dekorateuren in Python 2.6 und habe Probleme, sie zum Laufen zu bringen. Hier ist meine Klassendatei:

class testDec:

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

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

Ich dachte, dies würde bedeuten, xwie eine Eigenschaft zu behandeln , aber diese Funktionen beim Abrufen und Festlegen aufzurufen. Also habe ich IDLE gestartet und es überprüft:

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

Offensichtlich funktioniert der erste Aufruf wie erwartet, da ich den Getter aufrufe und es keinen Standardwert gibt und er fehlschlägt. OK, gut, ich verstehe. Der Aufruf zum Zuweisen t.x = 5scheint jedoch eine neue Eigenschaft zu erstellen x, und jetzt funktioniert der Getter nicht mehr!

Was vermisse ich?

TR.
quelle
6
Bitte benennen Sie Klassen mit einem Großbuchstaben. Sie können Kleinbuchstaben für Variablen und Moduldateien verwenden.
S.Lott

Antworten:

306

Sie scheinen in Python 2 klassische Klassen alten Stils zu verwenden . Damit Eigenschaften ordnungsgemäß funktionieren, müssen Sie stattdessen Klassen neuen Stils verwenden (in Python 2 müssen Sie von erbenobject ). Erklären Sie einfach Ihre Klasse als 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

Es klappt:

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

Ein weiteres Detail, das Probleme verursachen kann, besteht darin, dass beide Methoden denselben Namen benötigen, damit die Eigenschaft funktioniert. Wenn Sie den Setter mit einem anderen Namen wie diesem definieren, funktioniert dies nicht :

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

Und eine weitere Sache, die zunächst nicht ganz leicht zu erkennen ist, ist die Reihenfolge: Der Getter muss zuerst definiert werden . Wenn Sie zuerst den Setter definieren, wird eine name 'x' is not definedFehlermeldung angezeigt.

nosklo
quelle
4
In Python3 ist es nicht mehr nötig - "klassische" Klassen sind nur mit
Setzern in Ordnung
20
Es funktioniert in Python 3, da jede Klasse eine Klasse neuen Stils ist und es keine "klassischen" Klassen mehr gibt.
Craigds
Ich denke, man sollte eine Warnung / einen Fehler erhalten, wenn der Dekorateur in diesem Fall nicht richtig verarbeitet wird.
stfn
82

Nur ein Hinweis für andere Leute, die hier auf der Suche nach dieser Ausnahme stolpern: Beide Funktionen müssen denselben Namen haben. Wenn Sie die Methoden wie folgt benennen, führt dies zu einer Ausnahme:

@property
def x(self): pass

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

Geben Sie stattdessen beiden Methoden den gleichen Namen

@property
def x(self): pass

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

Es ist auch wichtig zu beachten, dass die Reihenfolge der Erklärung von Bedeutung ist. Der Getter muss vor dem Setter in der Datei definiert werden, sonst erhalten Sie einenNameError: name 'x' is not defined

Andrew Hows
quelle
Dies wird wahrscheinlich die "neue" beste Antwort werden, mit immer mehr Leuten auf Python 3 ...
trpt4him
5
Diese Antwort ist großartig. Ich denke, um vollständig zu sein, wäre es großartig, wenn Sie bemerken könnten, dass die Reihenfolge wichtig ist, das heißt, der Getter muss vor dem Setter im Code stehen. Andernfalls würden Sie eine NameError: name 'x' is not definedAusnahme erhalten.
Eguaio
Danke dafür. Ich habe gerade den größten Teil eines Tages damit verschwendet. Mir ist nie in den Sinn gekommen, dass die Methoden gleich genannt werden müssen. Wirklich frustrierende kleine Redewendung!
Ajkavanagh
Um das "Warum" hinter dieser Antwort zu verstehen, lesen Sie die endgültige Dokumentation hier: docs.python.org/2/library/functions.html#property Beachten Sie insbesondere den "genau äquivalenten" Teil.
Evgeni Sergeev
23

Sie müssen Klassen neuen Stils verwenden, indem Sie Ihre Klasse vom Objekt ableiten:

class testDec(object):
   ....

Dann sollte es funktionieren.

MrTopf
quelle
Alles oben war vorhanden. Dies war für mich in Python 2.7.x erforderlich, damit Eigenschaften in Klassen richtig gekickt werden konnten.
Drone Brain
12

Für den Fall, dass jemand von Google hierher kommt, möchte ich zusätzlich zu den obigen Antworten hinzufügen, dass dies besondere Aufmerksamkeit erfordert, wenn Sie den Setter aus der __init__Methode Ihrer Klasse aufrufen, die auf dieser Antwort basiert. Speziell:

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
Akotian
quelle
Nicht nur von der __init__Methode, sondern von jeder anderen Methode in der Klasse.
Mia