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'(简单调试语句)的编辑。尽管使用print语句当然是合理的,但文档字符串中没有任何伤害或错误,也完全不会影响问题。
约翰Y

Answers:


106

该文档说明了以下有关使用装饰器形式的信息property

确保为附加功能赋予与原始属性相同的名称(在这种情况下为x)。

我不知道为什么会这样,因为如果您将其property用作函数来返回属性,则可以随意调用这些方法。

因此,您需要将代码更改为以下内容:

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

太好了,我在另一个地方看到了这个,但是假设他们可能对python缺少方法重载一无所知。实际上看起来比“ set_x”名称更干净!:)
Casey Kuball 2012年

1
“设置”字符串的作用是什么?
zakdances 2013年

我遇到了同样的问题,我更改了属性的名称,却忘记了更改装饰器,这使我有时会错过的东西感到惊讶。感谢您的回答:)
艾琳(Erin)2013年

有人了解这种限制的原因是什么?
flnk 2014年

1
@flonk:这是相当技术性的,但是Python使用方法名称(def在线)作为属性名称有点神奇。因此,在OP的情况下,他定义了一个称为set_x并将其装饰为setter的方法,实际上他创建了一个名为settable的属性set_x!因此,c.x = 10他能做的是代替c.set_x = 10。请注意,装饰器中的名称无关紧要。如果他有两个属性,x并且y,他可以很容易地使用@x.setter@y.setterset_x(一个值,则分配给到一个setter属性self._x)。
约翰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
>>> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.