"Не вдається встановити атрибут" із властивостями нового стилю в Python


75

Я намагаюся використовувати декларацію властивостей нового стилю:

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

що я роблю не так? PS: Декларація в старому стилі прекрасно працює.


Я відкотив редагування, яке перетворює 'setting'(документ) на print 'setting'(простий налагоджувальний оператор). Незважаючи на те, що цілком правдоподібно, що було призначено заяву для друку, у документації немає шкоди чи помилок, і це взагалі не впливає на питання.
Джон Й

Відповіді:


106

Документація говорить про використання декоратора у формі property:

Обов’язково надайте додатковим функціям те саме ім’я, що і оригінальна властивість (у цьому випадку x).

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

Тому вам потрібно змінити свій код на такий:

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

Ага, чудово, я бачив це в іншому місці, але припускав, що вони можуть не знати про відсутність у python перевантаження методів. Насправді це виглядає чистіше, ніж ім'я 'set_x'! :)
Кейсі Кубалл

1
Для чого потрібен рядок 'setting'?
zakdances

У мене була та сама проблема, коли я змінив назву власності і забув змінити декоратора - дивовижні речі, за якими інколи можу пропустити. Дякую за відповідь :)
Ерін

Хтось розуміє, в чому причина цього обмеження?
флонк

1
@flonk: Це досить технічно, але Python робить трохи магії, щоб використовувати ім'я методу (у defрядку) як ім'я атрибута. Отже, у випадку з OP, коли він визначив викликаний метод set_xі оформив його як сеттер, він фактично створив атрибут settable з назвою set_x! Таким чином, замість того c.x = 10, що він міг зробити, це c.set_x = 10. Зверніть увагу, що ім’я в декораторі не має значення. Якби він мав дві властивості, xі y, він міг би так само легко використовувати @x.setterабо @y.setterперетворити set_xна атрибут сеттера (який присвоює значення self._x).
John Y


6

Коли ви викликаєте @ x.setter, @ x.getter або @ x.deleter, ви створюєте новий об'єкт властивості і надаєте йому ім'я функції, яку ви декоруєте. Тож насправді важливо лише те, що коли ви востаннє використовували декоратор @ x. * Er у визначенні класу, він мав ім’я, яке ви насправді хочете використовувати. Але оскільки це призведе до того, що ваш простір імен класу буде забруднений неповними версіями властивості, яку ви хочете використовувати, найкраще використовувати ту саму назву, щоб очистити їх.


1

Якщо ви не хочете зайвого _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
>>> 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.