Mengapa @ foo.setter dengan Python tidak berfungsi untuk saya?

155

Jadi, saya bermain dengan dekorator di Python 2.6, dan saya mengalami kesulitan untuk membuatnya bekerja. Ini file kelas saya:

class testDec:

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

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

Apa yang saya pikir dimaksudkan adalah memperlakukan xseperti properti, tetapi panggil fungsi ini di get and set. Jadi, saya menyalakan IDLE dan memeriksanya:

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

Jelas panggilan pertama berfungsi seperti yang diharapkan, karena saya memanggil pengambil, dan tidak ada nilai default, dan gagal. Oke, bagus, saya mengerti. Namun, panggilan untuk menetapkan t.x = 5tampaknya membuat properti baru x, dan sekarang pengambil tidak berfungsi!

Apa yang saya lewatkan?

TR.
sumber
6
Sebutkan Kelas Dengan Huruf Huruf Besar. Anda dapat menggunakan huruf kecil untuk variabel dan file modul.
S.Lott

Jawaban:

306

Anda tampaknya menggunakan kelas gaya lama klasik di python 2. Agar properti berfungsi dengan benar, Anda harus menggunakan kelas gaya baru (di python 2 Anda harus mewarisi dariobject ). Cukup nyatakan kelas Anda sebagai 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

Berhasil:

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

Detail lain yang mungkin menyebabkan masalah adalah bahwa kedua metode membutuhkan nama yang sama agar properti berfungsi. Jika Anda mendefinisikan setter dengan nama yang berbeda seperti ini, itu tidak akan berfungsi :

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

Dan satu hal lagi yang tidak sepenuhnya mudah dikenali pada awalnya, adalah urutannya: Sang pengambil harus ditentukan terlebih dahulu . Jika Anda menentukan setter terlebih dahulu, Anda mendapatkan name 'x' is not definedkesalahan.

nosklo
sumber
4
Dalam Python3 tidak perlu lagi - "klasik" kelas hanya ok dengan setter :-)
Eenoku
20
Ia bekerja di python 3 karena setiap kelas adalah kelas gaya baru, tidak ada kelas 'klasik' lagi.
craigds
Saya pikir seseorang harus mendapatkan peringatan / kesalahan, jika dekorator tidak diproses dengan benar dalam kasus ini.
stfn
82

Hanya catatan untuk orang lain yang tersandung di sini mencari pengecualian ini: kedua fungsi harus memiliki nama yang sama. Memberi nama metode sebagai berikut akan menghasilkan pengecualian:

@property
def x(self): pass

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

Sebaliknya berikan kedua metode nama yang sama

@property
def x(self): pass

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

Penting juga untuk dicatat bahwa urutan deklarasi penting. Pengambil harus ditentukan sebelum setter dalam file atau Anda akan mendapatkanNameError: name 'x' is not defined

Andrew Hows
sumber
Ini mungkin akan menjadi jawaban terbaik "baru", dengan semakin banyak orang menggunakan Python 3 ...
trpt4him
5
Jawaban ini bagus. Saya pikir itu harus lengkap, akan lebih bagus jika Anda dapat berkomentar bahwa urutan itu penting, artinya, pengambil harus ada sebelum setter dalam kode. Jika tidak, Anda akan mendapatkan NameError: name 'x' is not definedpengecualian.
eguaio
Terima kasih untuk ini. Saya hanya menyia-nyiakan bagian terbaik dari hari ini. Tidak pernah terpikir oleh saya bahwa metode harus disebut hal yang sama. Ungkapan kecil yang benar-benar membuat frustrasi!
ajkavanagh
Untuk memahami "mengapa" di balik jawaban ini, baca dokumentasi definitif di sini: docs.python.org/2/library/functions.html#property Terutama perhatikan bagian "persis setara".
Evgeni Sergeev
23

Anda perlu menggunakan kelas gaya baru yang Anda lakukan dengan menurunkan kelas Anda dari objek:

class testDec(object):
   ....

Maka itu harus bekerja.

MrTopf
sumber
Segala sesuatu di atas ada di tempatnya. Ini diperlukan bagi saya di python 2.7.x untuk properti di kelas untuk menendang dengan benar.
Drone Brain
12

Jika ada yang datang ke sini dari google, di samping jawaban di atas saya ingin menambahkan bahwa ini perlu perhatian ketika meminta setter dari __init__metode kelas Anda berdasarkan jawaban ini Secara khusus:

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
akotian
sumber
Bukan hanya dari __init__metode, tetapi fron metode lain di kelas.
Mia