'Attribut kann nicht festgelegt werden' mit neuen Eigenschaften in Python

75

Ich versuche, die Deklaration neuer Eigenschaften zu verwenden:

class C(object):
    def __init__(self):
        self._x = 0

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

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

if __name__ == '__main__':
    c = C()
    print c.x
    c.x = 10
    print c.x

und sehen Sie Folgendes in der Konsole:

pydev debugger: starting
getting
0
File "\test.py", line 55, in <module>
c.x = 10
AttributeError: can't set attribute

Was mache ich falsch? PS: Die Deklaration im alten Stil funktioniert einwandfrei.

Maxim Popravko
quelle
Ich habe die Bearbeitung zurückgesetzt, aus der 'setting'(eine Dokumentzeichenfolge) print 'setting'(eine einfache Debugging-Anweisung) wird. Es ist sicherlich plausibel, dass die print-Anweisung beabsichtigt war, aber es gibt keinen Schaden oder Fehler in der Dokumentzeichenfolge und es hat keinerlei Auswirkungen auf die Frage.
John Y

Antworten:

105

In der Dokumentation wird Folgendes zur Verwendung der Dekorationsform beschrieben property:

Stellen Sie sicher, dass die zusätzlichen Funktionen denselben Namen wie die ursprüngliche Eigenschaft haben (in diesem Fall x).

Ich habe keine Ahnung, warum dies so ist, da propertydie Methoden aufgerufen werden können, wenn Sie als Funktion ein Attribut zurückgeben.

Sie müssen Ihren Code also wie folgt ändern:

@x.setter
def x(self, value):
    'setting'
    self._x = value
Dave Webb
quelle
Ah toll, ich habe das an einem anderen Ort gesehen, aber ich nahm an, dass sie nichts über Pythons mangelnde Methodenüberladung wissen. Das sieht tatsächlich sauberer aus als ein 'set_x'-Name! :)
Casey Kuball
1
Wofür ist die 'Einstellungs'-Zeichenfolge?
Zakdances
Ich hatte das gleiche Problem, bei dem ich den Namen der Unterkunft geändert und vergessen habe, den Dekorateur zu ändern - erstaunlich, was ich manchmal vermissen kann. Danke für die Antwort :)
Erin
Versteht jemand, was der Grund für diese Einschränkung ist?
Flonk
@flonk: Es ist ziemlich technisch, aber Python macht ein bisschen Magie, um den Methodennamen (in der defZeile) als Attributnamen zu verwenden. In OPs Fall, in dem er eine Methode namens aufgerufen set_xund als Setter dekoriert hat, hat er tatsächlich ein einstellbares Attribut namens set_x! Also, anstatt c.x = 10was er tun könnte, ist c.set_x = 10. Beachten Sie, dass der Name im Dekorateur keine Rolle spielt. Wenn er zwei Eigenschaften hätte xund y, könnte er genauso gut ein Setter-Attribut verwenden @x.setteroder @y.setteres set_xin ein Setter-Attribut verwandeln (das einen Wert zuweist self._x).
John Y
14

Die Setter-Methode muss denselben Namen wie der Getter haben. Keine Sorge, der Dekorateur weiß, wie man sie voneinander unterscheidet.

@x.setter
def x(self, value):
 ...
Ignacio Vazquez-Abrams
quelle
6

Wenn Sie @ x.setter, @ x.getter oder @ x.deleter aufrufen, erstellen Sie ein neues Eigenschaftsobjekt und geben ihm den Namen der Funktion, die Sie dekorieren. Alles, was wirklich zählt, ist, dass das letzte Mal, wenn Sie einen @ x. * Er-Dekorator in der Klassendefinition verwenden, dieser den Namen hat, den Sie tatsächlich verwenden möchten. Da Ihr Klassennamensraum dadurch jedoch mit unvollständigen Versionen der Eigenschaft verschmutzt wird, die Sie verwenden möchten, verwenden Sie zum Bereinigen am besten denselben Namen.

Kyle
quelle
1

Wenn Sie den zusätzlichen _xNamensschlitz nicht möchten , können Sie hier einen komplexen kleinen Trick
ausführen : (getestet mit Py34)

>>> class C(object):
    __slots__ = ['x'] # create a member_descriptor
    def __init__( self ):
        self.x = 0
        # or use this if you don't want to call x_setter:
        #set_x( self, 0 )


>>> get_x = C.x.__get__ # member_descriptor getter
>>> set_x = C.x.__set__ # member_descriptor setter
>>> # define custom wrappers:
>>> def x_getter( self ):
    print('getting')
    return get_x( self )

>>> def x_setter( self, value ):
    print('setting')
    set_x( self, value )


>>> C.x = property( x_getter, x_setter ) # replace the member_descriptor
>>> c = C()
setting
>>> print(c.x)
getting
0
>>> c.x = 10
setting
>>> 
Tcll
quelle