'Не удается установить атрибут' с новыми свойствами стиля в Python


Я пытаюсь использовать объявление свойств нового стиля:

class C(object):
    def __init__(self):
        self._x = 0

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

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

if __name__ == '__main__':
    c = C()
    print c.x
    c.x = 10
    print c.x

и видим следующее в консоли:

pydev debugger: starting
getting
0
File "test.py", line 55, in <module>
c.x = 10
AttributeError: can't set attribute

что я делаю не так? P. S.: В старом стиле декларация нормально работает.

4 59

4 ответа:

в документации говорится следующее об использовании декоратора формы property:

убедитесь, что дополнительные функции имеют то же имя, что и исходное свойство (x в этом случае.)

Я не знаю, почему это так, если вы используете property как функция для возврата атрибута методы могут быть вызваны как угодно.

поэтому вам нужно изменить свой код следующим образом:

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

метод setter должен иметь то же имя, что и геттер. Не волнуйтесь, декоратор знает, как отличить их друг от друга.

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

когда вы вызываете @x. setter, @x.getter или @x.deleter, вы создаете новый объект свойства и даете ему имя функции, которую вы украшаете. Так что на самом деле все, что имеет значение, это то, что в последний раз, когда вы используете декоратор @x.*er в определении класса, у него есть имя, которое вы действительно хотите использовать. Но поскольку это приведет к загрязнению пространства имен вашего класса неполными версиями свойства, которое вы хотите использовать, лучше всего использовать одно и то же имя для их очистки.

Если вы не хотите лишний _x имя слот, вот сложный маленький трюк, который вы можете сделать:
(проверено с Py34)

>>> class C(object):
    __slots__ = ['x'] # create a member_descriptor
    def __init__( self ):
        self.x = 0
        # or use this if you don't want to call x_setter:
        #set_x( self, 0 )


>>> get_x = C.x.__get__ # member_descriptor getter
>>> set_x = C.x.__set__ # member_descriptor setter
>>> # define custom wrappers:
>>> def x_getter( self ):
    print('getting')
    return get_x( self )

>>> def x_setter( self, value ):
    print('setting')
    set_x( self, value )


>>> C.x = property( x_getter, x_setter ) # replace the member_descriptor
>>> c = C()
setting
>>> print(c.x)
getting
0
>>> c.x = 10
setting
>>>