Por que o @ foo.setter em Python não trabalho para mim?
-
11-09-2019 - |
Pergunta
Então, eu estou jogando com decoradores em Python 2.6, e eu estou tendo alguns problemas começá-los para o trabalho. Aqui está o meu arquivo de 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
O que eu pensei que isso significava é x
tratar como uma propriedade, mas chamar essas funções em get e set. Então, eu despediu-se IDLE e verificou-lo:
>>> 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
É claro que a primeira chamada funciona como esperado, uma vez que eu chamo o getter, e não há nenhum valor padrão, e ele falhar. OK, bem, eu entendo. No entanto, a chamada para t.x = 5
atribuir parece criar um novo x
propriedade, e agora o getter não funciona!
O que eu estou ausente?
Solução
Você parece estar usando aulas clássicas de estilo antigo em python 2. para que propriedades ao trabalho será necessário o uso de classes new-style vez (em python 2 você deve herdar do object
). Apenas declarar sua classe 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
Ele 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
>>>
Outro detalhe que pode causar problemas é que ambos os métodos precisam do mesmo nome para a propriedade ao trabalho. Se você definir o setter com um nome diferente como este não vai funcionar :
@x.setter
def x_setter(self, value):
...
E mais uma coisa que não é completamente fácil de detectar no início, é a ordem: O getter deve ser definido pela primeira vez . Se você definir o setter primeiro, receberá o erro name 'x' is not defined
.
Outras dicas
Apenas uma nota para outras pessoas que encontraram aqui à procura de uma exceção: as duas funções precisam ter o mesmo nome. Nomear os métodos da seguinte resultará em uma exceção:
@property
def x(self): pass
@x.setter
def x_setter(self, value): pass
Em vez disso dar a ambos os métodos o mesmo nome
@property
def x(self): pass
@x.setter
def x(self, value): pass
Também é importante notar que a ordem das matérias de declaração. O getter deve ser definido antes do setter no arquivo, ou então você vai ter um NameError: name 'x' is not defined
Você precisa usar as classes de estilo novo, que você faz por derivar sua classe de objeto:
class testDec(object):
....
Em seguida, ele deve funcionar.
Caso alguém vem aqui de google, além das respostas acima, gostaria de acrescentar que este precisa de atenção especial ao chamar o setter do método __init__
de sua classe com base em esta resposta
Especificamente:
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