Por que @ foo.setter em Python não funciona para mim?


155

Então, estou brincando com os decoradores no Python 2.6 e estou tendo alguns problemas para fazê-los funcionar. 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 é tratar xcomo uma propriedade, mas chame essas funções de get e set. Então, iniciei o IDLE e verifiquei:

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

Claramente, a primeira chamada funciona como esperado, desde que eu chamo o getter, e não há valor padrão, e ele falha. OK, bom, eu entendo. No entanto, a chamada para atribuir t.x = 5parece criar uma nova propriedade xe agora o getter não funciona!

o que estou perdendo?


6
Nomeie Classes Com Uma Letra Maiúscula. você pode usar letras minúsculas para variáveis ​​e arquivos de módulo.
— 315/09 S.Lott

Respostas:


306

Parece que você está usando classes clássicas de estilo antigo no python 2. Para que as propriedades funcionem corretamente, você precisa usar classes de estilo novo (no python 2 você deve herdarobject ). Apenas declare 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

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 os dois métodos precisam do mesmo nome para que a propriedade funcione. Se você definir o setter com um nome diferente como este, não funcionará :

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

E mais uma coisa que não é completamente fácil de identificar a princípio, é a ordem: o getter deve ser definido primeiro . Se você definir o setter primeiro, obtém name 'x' is not defined erro.


4
Em Python3 é necessário não mais - as classes "clássicos" são apenas ok com setters :-)
— Eenoku

20
Ele funciona no python 3 porque cada classe é uma classe de novo estilo, não há mais classes 'clássicas'.
— craigds 23/06

Eu acho que é preciso receber um aviso / erro, se o decorador não for processado corretamente neste caso.
— Stfn 19/07/19

82

Apenas uma observação para outras pessoas que tropeçam aqui procurando por essa exceção: ambas as funções precisam ter o mesmo nome. Nomear os métodos da seguinte maneira resultará em uma exceção:

@property
def x(self): pass

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

Em vez disso, dê a ambos os métodos o mesmo nome

@property
def x(self): pass

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

Também é importante observar que a ordem da declaração é importante. O getter deve ser definido antes do setter no arquivo, ou você receberá umaNameError: name 'x' is not defined


Este é provavelmente vai se tornar o "novo" melhor resposta, com mais e mais pessoas em Python 3 ...
— trpt4him

5
Esta resposta é ótima. Eu acho que, para ser completo, seria ótimo se você pudesse observar que a ordem é importante, ou seja, o getter deve estar antes do setter no código. Caso contrário, você receberá uma NameError: name 'x' is not definedexceção.
— eguaio

Obrigado por isso. Eu perdi a melhor parte de um dia nisso. Nunca me ocorreu que os métodos devessem ser chamados da mesma coisa. Pequeno idioma realmente frustrante!
— ajkavanagh

Para entender o "porquê" por trás desta resposta, leia a documentação definitiva aqui: docs.python.org/2/library/functions.html#property Observe especialmente a parte "exatamente equivalente".
— Evgeni Sergeev

23

Você precisa usar classes de novo estilo, derivando sua classe do objeto:

class testDec(object):
   ....

Então deve funcionar.


Tudo acima estava no lugar. Isso foi necessário para mim no python 2.7.x para que as propriedades nas classes fossem executadas corretamente.
— Drone Brain

12

Caso alguém venha aqui do google, além das respostas acima, gostaria de acrescentar que isso precisa de muita atenção ao chamar o setter do __init__método da sua classe com base nesta 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

Não apenas do __init__método, mas de qualquer outro método da classe.
— Mia
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.