Чому @ foo.setter в Python не працює для мене?


155

Отже, я граю з декораторами в 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як властивість, але викликати ці функції на get and set. Отже, я запустив 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, і тепер геть не працює!

Що я пропускаю?


6
Будь ласка, назвіть класи з великого письма. ви можете використовувати малі регістри для змінних та модульних файлів.
S.Lott

Відповіді:


306

Здається, ви використовуєте класичні класи старого стилю в 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помилку.


4
У Python3 більше не потрібно - "класичні" класи просто
налаштовані

20
Він працює в python 3, тому що кожен клас є класом нового стилю, більше немає класичних класів.
Крейгс

Я думаю, що слід отримати попередження / помилку, якщо декоратор не буде оброблений належним чином.
stfn

82

Просто примітка для інших людей, які тут натрапляють на пошуки цього винятку: обидві функції повинні мати однакове ім’я. Названня методів наступним чином призведе до винятку:

@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

Важливо також зазначити, що порядок декларації має значення. Getter має бути визначений перед сеттером у файлі, інакше ви отримаєтеNameError: name 'x' is not defined


Це, мабуть, стане "новою" найкращою відповіддю, де більше і більше людей на Python 3 ...
trpt4him

5
Ця відповідь чудова. Я думаю, що для завершення було б чудово, якщо ви зможете зауважити, що порядок важливий, тобто геттер повинен бути перед сеттером у коді. Інакше ви отримаєте NameError: name 'x' is not definedвиняток.
eguaio

Дякую за це Я просто витратив найкращу частину дня на це. Мені ніколи не спадало на думку, що методи повинні називатися одним і тим же. Дійсно розчаровує маленьку ідіому!
ajkavanagh

Щоб зрозуміти "чому" за цією відповіддю, прочитайте тут остаточну документацію: docs.python.org/2/library/functions.html#property Особливо відзначте "точно еквівалентну" частину.
Євгеній Сергєєв

23

Вам потрібно використовувати класи нового стилю, які ви робите, виводячи клас з об'єкта:

class testDec(object):
   ....

Тоді це має спрацювати.


Все вище було на місці. Це було потрібно для мене в python 2.7.x для властивостей у класах, щоб правильно бити.
Мозок

12

Якщо хтось приходить сюди з 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

Не тільки з __init__методу, але і з будь-якого іншого методу в класі.
Міа
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.