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?


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.


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


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.


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

Bukan hanya dari __init__metode, tetapi fron metode lain di kelas.
Mia
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.