Dlaczego @ foo.setter w Pythonie nie działa dla mnie?

Więc bawię się dekoratorami w Pythonie 2.6 i mam problem z doprowadzeniem ich do pracy. Oto plik mojej klasy:

class testDec:

    @property
    def x(self): 
        print 'called getter'
        return self._x

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

Myślałem, że to oznacza traktowanie x Jak właściwości, ale wywołanie tych funkcji na get I set. Więc odpaliłem bezczynnie i sprawdziłem:

>>> 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

Oczywiście pierwsze wywołanie działa zgodnie z oczekiwaniami, ponieważ wywołuję getter, i nie ma wartości domyślnej, i to się nie powiedzie. Dobrze, rozumiem. Jednak wywołanie przypisania t.x = 5 wydaje się tworzyć nowe property x, a teraz getter nie działa!

Co mi umyka?
Author: smci, 2009-02-28

4 answers

Wydaje się, że używasz klasycznych klas starego stylu w Pythonie 2. Aby Właściwości działały poprawnie, musisz użyć klas nowego stylu (w Pythonie 2 musisz dziedziczyć z object). Po prostu zadeklaruj swoją klasę jako 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

Działa:

>>> 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
>>> 

Innym szczegółem, który może powodować problemy, jest to, że obie metody wymagają tej samej nazwy, aby właściwość działała. jeśli zdefiniujesz seter z inną nazwą, jak ta, to nie praca:

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

I jeszcze jedna rzecz, która nie jest łatwa do zauważenia na początku, to kolejność: getter musi być zdefiniowany jako pierwszy. Jeśli najpierw zdefiniujesz setter, otrzymasz błąd name 'x' is not defined.

 267
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-06 17:55:32

Tylko uwaga dla innych ludzi, którzy natkną się tutaj szukając tego wyjątku: obie funkcje muszą mieć tę samą nazwę. Nazwanie metod w następujący sposób spowoduje wyjątek:

@property
def x(self): pass

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

Zamiast tego nadaj obu metodom tę samą nazwę

@property
def x(self): pass

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

Ważne jest również, aby pamiętać, że kolejność deklaracji ma znaczenie. Getter musi być zdefiniowany przed setterem w pliku, w przeciwnym razie otrzymasz NameError: name 'x' is not defined

 68
Author: Andrew Hows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-18 20:26:53

Musisz użyć klas nowego stylu, które robisz, wyprowadzając klasę z obiektu:

class testDec(object):
   ....
To powinno zadziałać.
 22
Author: MrTopf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-28 14:43:22

W przypadku, gdyby ktoś przyszedł tutaj z google, oprócz powyższych odpowiedzi chciałbym dodać, że wymaga to uważnej uwagi przy wywoływaniu setera z metody __init__ twojej klasy opartej na tej odpowiedzi Konkretnie:

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
 9
Author: akotian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:16