为什么Python中的@ foo.setter对我不起作用?


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和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,并且没有默认值,并且失败。好的,我了解。但是,对assign的调用t.x = 5似乎会创建一个新属性x,而现在getter不起作用!

我想念什么?


6
请用大写字母命名课程。您可以将小写字母用于变量和模块文件。
S.Lott

Answers:


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中,不再需要-“ setter”类就可以使用“经典”类了:-)
Eenoku 2015年

20
它在python 3中有效,因为每个类都是一个新式类,现在不再有“经典”类。
craigds

我认为,如果在这种情况下未正确处理装饰器,则应该收到警告/错误。
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

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


这可能将成为“新”最佳答案,越来越多的人使用Python 3 ...
trpt4him

5
这个答案很好。我认为,为了完整起见,如果您可以指出顺序很重要,那就太好了,也就是说,getter必须在代码中的setter之前。否则,您将获得NameError: name 'x' is not defined例外。
eguaio

谢谢你 我只是浪费了一天的大部分时间。我从没想到必须将这些方法称为同一件事。真是令人沮丧的小成语!
ajkavanagh

要了解此答案背后的“原因”,请在此处阅读权威文档:docs.python.org/2/library/functions.html#property特别注意“完全等效”部分。
Evgeni Sergeev '18

23

您需要通过从对象派生类来使用新型类:

class testDec(object):
   ....

然后它应该工作。


以上所有内容均已就绪。我需要在python 2.7.x中正确启动类中的属性。
Drone Brain '18

12

如果有人来自谷歌,除了上面的答案,我想补充一点__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__方法中,而且从类中的任何其他方法中取消。
米娅
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.