Почему у меня не работает @ foo.setter в Python?

Итак, я играю с декораторами в 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

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

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


person TR.    schedule 28.02.2009    source источник
comment
Назовите классы заглавными буквами. вы можете использовать строчные буквы для переменных и файлов модулей.   -  person S.Lott    schedule 28.02.2009


Ответы (4)


Похоже, вы используете классический старый- классы стилей в 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 ошибку.

person nosklo    schedule 28.02.2009
comment
В Python3 это больше не нужно - классические классы подходят сеттерам :-) - person Eenoku; 12.06.2015
comment
Он работает в Python 3, потому что каждый класс - это класс нового стиля, «классических» классов больше нет. - person craigds; 24.06.2015
comment
Думаю, должно появиться предупреждение / ошибка, если декоратор в этом случае обработан некорректно. - person stfn; 19.07.2019

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

@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

person Andrew Hows    schedule 30.06.2013
comment
Вероятно, это станет новым лучшим ответом, когда все больше и больше людей будут использовать Python 3 ... - person trpt4him; 09.07.2016
comment
Это отличный ответ. Я думаю, что для полноты было бы замечательно, если бы вы заметили, что порядок важен, то есть геттер должен стоять перед сеттером в коде. В противном случае вы получите NameError: name 'x' is not defined исключение. - person eguaio; 18.07.2016
comment
Спасибо за это. Я просто потратил на это лучшую часть дня. Мне никогда не приходило в голову, что методы нужно называть одинаково. Действительно разочаровывающая маленькая идиома! - person ajkavanagh; 25.10.2016
comment
Чтобы понять причину этого ответа, прочтите исчерпывающую документацию здесь: docs.python. org / 2 / library / functions.html # property Особо обратите внимание на точно эквивалентную часть. - person Evgeni Sergeev; 22.03.2018

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

class testDec(object):
   ....

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

person MrTopf    schedule 28.02.2009
comment
Все вышеперечисленное было на месте. Это было необходимо мне в python 2.7.x, чтобы свойства в классах работали правильно. - person Drone Brain; 18.10.2018

Если кто-то приходит сюда из 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
person akotian    schedule 11.02.2015
comment
Не только из метода __init__, но из любого другого метода в классе. - person Mia; 02.04.2018
comment
Если вы используете Python3, вам следует использовать print("xxxxx") вместо print "xxxxx" - person baponkar; 07.05.2021