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

TR.*_*TR. 153 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
Run Code Online (Sandbox Code Playgroud)

我认为这意味着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
Run Code Online (Sandbox Code Playgroud)

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

我错过了什么?

nos*_*klo 301

您似乎在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
Run Code Online (Sandbox Code Playgroud)

有用:

>>> 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
>>> 
Run Code Online (Sandbox Code Playgroud)

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

@x.setter
def x_setter(self, value):
    ...
Run Code Online (Sandbox Code Playgroud)

还有一件事情,一开始并不容易发现,是顺序:必须首先定义吸气剂.如果先定义setter,则会 name 'x' is not defined出现错误.

  • 它适用于python 3,因为每个类都是一个新风格的类,不再有"经典"类. (18认同)
  • 在Python3中,不再需要 - "经典"类对于setter来说是正常的:-) (4认同)

And*_*ows 77

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

@property
def x(self): pass

@x.setter
def x_setter(self, value): pass
Run Code Online (Sandbox Code Playgroud)

而是给两个方法命名相同

@property
def x(self): pass

@x.setter
def x(self, value): pass
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案很棒.我认为要完成,如果你能说顺序很重要,那就好了,就是说,getter必须在代码中的setter之前.否则,你会得到一个`NameError:name'x'未定义`异常. (4认同)
  • 随着越来越多的人使用 Python 3,这可能会成为“新”的最佳答案...... (2认同)

MrT*_*opf 22

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

class testDec(object):
   ....
Run Code Online (Sandbox Code Playgroud)

然后它应该工作.


ako*_*ian 10

如果有人从谷歌来到这里,除了上面的答案,我想补充一点,__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
Run Code Online (Sandbox Code Playgroud)