为什么Python中的@ foo.setter不适合我?

时间:2009-02-28 14:33:51

标签: python decorator new-style-class

所以,我正在使用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和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

显然,第一个调用按预期工作,因为我调用了getter,并且没有默认值,并且它失败了。好的,好的,我理解。但是,分配t.x = 5的调用似乎创建了一个新属性x,现在getter不起作用!

我错过了什么?

4 个答案:

答案 0 :(得分:286)

你好像在python 2中使用classic old-style classes。要使properties正常工作,你需要使用new-style classes(在python 2中你必须inherit from 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
>>> 

可能导致问题的另一个细节是两个方法都需要相同的名称才能使属性起作用。 如果您使用不同的名称定义setter,则无法使用

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

首先要发现的另一件事就是顺序:必须首先定义吸气剂 。如果先定义setter,则会出现name 'x' is not defined错误。

答案 1 :(得分:73)

请注意其他人在这里偶然发现寻找此异常:两个函数都需要具有相同的名称。如下命名方法将导致异常:

@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

同样重要的是要注意声明的顺序很重要。必须在文件中的setter之前定义getter,否则你将获得NameError: name 'x' is not defined

答案 2 :(得分:23)

您需要使用通过从对象派生类来执行的新式类:

class testDec(object):
   ....

然后它应该工作。

答案 3 :(得分:10)

如果有人从谷歌来到这里,除了上述答案之外,我想补充一点,在根据this answer的类__init__方法调用setter时需要特别注意 具体做法是:

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