Python'daki @ foo.setter benim için neden çalışmıyor?


155

Bu yüzden, Python 2.6'da dekoratörler ile oynuyorum ve onları çalıştırmakta zorlanıyorum. İşte benim sınıf dosya:

class testDec:

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

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

Bunun ne anlama geldiğini düşündüm, xbir özellik gibi davranmak , ancak get ve sette bu işlevleri çağırmak. Yani, IDLE'yi çalıştırdım ve kontrol ettim:

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

Açıkçası ilk çağrı beklendiği gibi çalışır, çünkü ben alıcı çağırır ve hiçbir varsayılan değer yoktur ve başarısız olur. Tamam, iyi, anladım. Ancak, atama çağrısı t.x = 5yeni bir mülk oluşturuyor gibi görünüyor xve şimdi alıcı çalışmıyor!

Neyi kaçırıyorum?


6
Lütfen Sınıfları Büyük Harfle Adlandırın. değişkenler ve modül dosyaları için küçük harf kullanabilirsiniz.
S.Lott

Yanıtlar:


306

Python 2'de klasik eski stil sınıfları kullanıyor gibi görünüyorsunuz. Özelliklerin düzgün çalışması için bunun yerine yeni stil sınıfları kullanmanız gerekir (python 2'de mirasobject almanız gerekir ). Sınıfınızı şu şekilde beyan edin 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

İşe yarıyor:

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

Sorunlara yol açabilecek diğer bir ayrıntı da, özelliğin çalışması için her iki yöntemin de aynı ada ihtiyacı olmasıdır. Ayarlayıcıyı böyle farklı bir adla tanımlarsanız, çalışmaz :

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

Ve başlangıçta tamamen kolay olmayan bir şey daha var, sipariş: Önce alıcı tanımlanmalıdır . Önce ayarlayıcıyı tanımlarsanız name 'x' is not definedhata alırsınız .


4
Python3'te artık gerekli değil - "klasik" sınıflar setters ile tamam
Eenoku

20
Python 3'te çalışır, çünkü her sınıf yeni bir sınıftır, artık 'klasik' sınıflar yoktur.
craigds

Dekoratör bu durumda doğru işlenmezse, bir uyarı / hata alması gerektiğini düşünüyorum.
stfn

82

Burada bu istisnayı arayan diğer insanlar için bir not: her iki işlevin de aynı ada sahip olması gerekir. Yöntemleri aşağıdaki gibi adlandırmak bir istisnaya neden olur:

@property
def x(self): pass

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

Bunun yerine her iki yönteme de aynı adı verin

@property
def x(self): pass

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

Deklarasyon sırasının önemli olduğunu da belirtmek önemlidir. Alıcı, dosyadaki ayarlayıcıdan önce tanımlanmalıdır; aksi takdirde,NameError: name 'x' is not defined


Bu muhtemelen Python 3 ile daha fazla insan ile "yeni" en iyi cevap olacak ...
trpt4him

5
Bu cevap harika. Ben tam olması için, eğer sipariş önemli olduğunu, yani alıcı kodunda setter önce olması gerektiğini söyleyebilirseniz harika olacağını düşünüyorum. Aksi takdirde, bir NameError: name 'x' is not definedistisna elde edersiniz .
eguaio

Bunun için teşekkürler. Sadece bir günün en iyi bölümünü bu konuda boşa harcadım. Metodların aynı şekilde adlandırılması gerektiği hiç aklıma gelmedi. Gerçekten sinir bozucu küçük deyim!
ajkavanagh

Bu cevabın arkasındaki "neden" i anlamak için, buradaki kesin belgeleri okuyun: docs.python.org/2/library/functions.html#property Özellikle "tam olarak eşdeğer" kısmı not edin.
Evgeni Sergeev

24

Sınıfınızı nesneden türeterek yaptığınız yeni stil sınıflarını kullanmanız gerekir:

class testDec(object):
   ....

O zaman işe yaramalı.


Yukarıdaki her şey yerinde idi. Bu, sınıflardaki mülklerin düzgün bir şekilde tekmelemesi için python 2.7.x'te benim için gerekliydi.
Drone Brain

12

Durumda herkes, yukarıdaki cevaplara ek olarak ben den ayarlayıcı çağrılırken bu dikkat ihtiyacı olduğunu eklemek istiyorum buradan google geliyor __init__dayanarak sınıfının yöntemine Bu cevap Özellikle:

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

Sadece __init__yöntemden değil, sınıftaki diğer yöntemlerden de önü.
Mia
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.