Почему @ foo.setter в Python не работает для меня? - PullRequest
147 голосов
/ 28 февраля 2009

Итак, я играю с декораторами в Python 2.6, и у меня возникают некоторые проблемы с их работой. Вот мой файл класса:

class testDec:

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

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

Я думал, что это означает, что x следует рассматривать как свойство, но вызывать эти функции при получении и установке. Итак, я запустил IDLE и проверил:

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

Ясно, что первый вызов работает, как и ожидалось, так как я вызываю метод получения, и по умолчанию значение отсутствует и происходит сбой. ОК, хорошо, я понимаю. Однако вызов присвоения t.x = 5 создает новое свойство x, и теперь метод получения не работает!

Чего мне не хватает?

Ответы [ 4 ]

288 голосов
/ 28 февраля 2009

Вы, кажется, используете классические классы старого стиля в python 2. Чтобы свойства работали правильно, вам нужно использовать классы нового стиля вместо этого (в Python 2 вы должны наследовать от object). Просто объявите свой класс как 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

Работает:

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

Еще одна деталь, которая может вызвать проблемы, заключается в том, что обоим методам необходимо одинаковое имя для работы свойства. Если вы определите сеттер с другим именем, как это, он не будет работать :

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

И еще одна вещь, которую поначалу не совсем легко обнаружить, это порядок: сначала следует определить получатель . Если вы сначала определите установщик, вы получите name 'x' is not defined error.

73 голосов
/ 01 июля 2013

Просто замечание для других людей, которые спотыкаются здесь, ища это исключение: обе функции должны иметь одинаковое имя. Наименование методов следующим образом приведет к исключению:

@property
def x(self): pass

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

Вместо этого дайте обоим методам одно и то же имя

@property
def x(self): pass

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

Также важно отметить, что порядок декларации имеет значение. Получатель должен быть определен перед установщиком в файле, иначе вы получите NameError: name 'x' is not defined

23 голосов
/ 28 февраля 2009

Вам нужно использовать классы нового стиля, которые вы делаете, наследуя свой класс от объекта:

class testDec(object):
   ....

Тогда это должно сработать.

10 голосов
/ 12 февраля 2015

В случае, если кто-нибудь пришел сюда из Google, в дополнение к вышеприведенным ответам я хотел бы добавить, что это требует тщательного внимания при вызове метода установки из __init__ метода вашего класса на основе этого ответа В частности:

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