Python: Apakah ini bentuk yang buruk untuk meningkatkan pengecualian dalam __init__?

128

Apakah dianggap sebagai bentuk yang buruk untuk meningkatkan pengecualian di dalam __init__? Jika demikian, lalu apa metode yang diterima untuk melempar kesalahan ketika variabel kelas tertentu diinisialisasi sebagai Noneatau dari tipe yang salah?

krizajb
sumber
Di C, itu bentuk buruk untuk memunculkan eksepsi pada destruktor, tetapi konstruktor harus baik-baik saja.
Calyth
6
@Calyth, Anda berarti C ++ - tidak ada konstruktor atau destructors di C.
Alex Martelli
4
... atau pengecualian, dalam hal ini.
Matt Wilding
@Calyth: lol, tolong hapus pernyataan tidak masuk akal yang dibuat oleh salah ketik yang salah ;-)
lpapp
4
@ app ya. C ++. FML. Dan saya tidak bisa mengedit komentar itu. Jadi internet akan mendokumentasikan kebodohan saya;)
Calyth

Jawaban:

159

Meningkatkan pengecualian di dalam __init__()benar-benar baik. Tidak ada cara lain yang baik untuk menunjukkan kondisi kesalahan dalam konstruktor, dan ada banyak contoh di perpustakaan standar tempat membangun objek dapat menimbulkan pengecualian.

Kelas kesalahan untuk dinaikkan, tentu saja, terserah Anda. ValueErrorlebih baik jika konstruktor melewati parameter yang tidak valid.

John Millikin
sumber
14
Pengecualian yang paling sering digunakan dalam konstruktor adalah ValueErrordan TypeError.
Denis Otkidach
9
Hanya menunjukkan bahwa __init__itu bukan konstruktor, itu intializer. Anda mungkin ingin mengedit baris. Tidak ada cara lain yang baik untuk menunjukkan kondisi kesalahan dalam sebuah konstruktor, ..
Haris
26

Memang benar bahwa satu-satunya cara yang tepat untuk menunjukkan kesalahan pada konstruktor adalah dengan meningkatkan pengecualian. Itulah sebabnya dalam C ++ dan dalam bahasa berorientasi objek lainnya yang telah dirancang dengan mempertimbangkan keselamatan pengecualian, destruktor tidak dipanggil jika pengecualian dilemparkan ke dalam konstruktor objek (artinya inisialisasi objek tidak lengkap). Ini sering tidak terjadi dalam bahasa scripting, seperti Python. Misalnya, kode berikut melempar AttributeError jika socket.connect () gagal:

class NetworkInterface:
    def __init__(self, address)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect(address)
        self.stream = self.socket.makefile()

    def __del__(self)
        self.stream.close()
        self.socket.close()

Alasannya adalah bahwa destruktor objek yang tidak lengkap dipanggil setelah upaya koneksi gagal, sebelum atribut stream diinisialisasi. Anda tidak boleh menghindari melemparkan pengecualian dari konstruktor, saya hanya mengatakan bahwa sulit untuk menulis sepenuhnya kode aman dengan Python. Beberapa pengembang Python menghindari penggunaan destruktor sama sekali, tapi itu masalah perdebatan lain.

Seppo Enarvi
sumber
Jawaban ini sangat berguna. Ini tidak hanya berbicara tentang "lampu hijau", tetapi menyebutkan contoh dengan "destruktor".
lpapp
Kode python Anda gagal __del__karena kode itu ditulis dengan buruk. Anda akan memiliki masalah yang sama persis jika Anda melakukannya this->p_socket->close()dalam destruktor di C ++. Di C ++, Anda tidak akan melakukan itu - Anda akan membiarkan objek anggota menghancurkan dirinya sendiri. Lakukan hal yang sama dengan python.
Eric
1
@ Eric Saya tidak akan memiliki masalah dalam C ++ karena C ++ tidak merusak objek yang belum diinisialisasi dengan benar . Sebenarnya ini adalah idiom yang sangat umum dalam C ++ untuk mengalokasikan sumber daya di konstruktor dan mengalokasikannya dalam destruktor . Anda menyarankan bahwa objek anggota menghancurkan dirinya sendiri (menghilangkan sumber daya dalam destruktornya) - maka masalah yang sama muncul ketika menulis kelas anggota.
Seppo Enarvi
11

Saya tidak melihat alasan mengapa itu seharusnya bentuk yang buruk.

Sebaliknya, salah satu hal yang dikenal sebagai pengecualian untuk mengembalikan kode kesalahan adalah bahwa kode kesalahan biasanya tidak dapat dikembalikan oleh konstruktor. Jadi setidaknya dalam bahasa seperti C ++, meningkatkan pengecualian adalah satu-satunya cara untuk memberi sinyal kesalahan.

Edan Maor
sumber
6

Perpustakaan standar mengatakan:

>>> f = file("notexisting.txt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 2] No such file or directory: 'notexisting.txt'

Juga saya tidak benar-benar melihat alasan mengapa itu harus dianggap sebagai bentuk yang buruk.

sth
sumber
3

Saya harus berpikir itu adalah kasus sempurna untuk ValueErrorpengecualian bawaan.

Teddy
sumber
2

Saya setuju dengan semua hal di atas.

Tidak ada cara lain untuk memberi sinyal bahwa ada yang salah dalam inisialisasi objek selain mengajukan pengecualian.

Di sebagian besar program, kelas di mana keadaan suatu kelas sepenuhnya bergantung pada input ke kelas itu, kita mungkin mengharapkan semacam ValueError atau TypeError untuk dinaikkan.

Kelas dengan efek samping (mis. Yang melakukan jaringan atau grafik) dapat menimbulkan kesalahan init jika (misalnya) perangkat jaringan tidak tersedia atau objek kanvas tidak dapat ditulis. Ini kedengarannya masuk akal bagi saya karena sering Anda ingin tahu tentang kondisi kegagalan sesegera mungkin.

Salim Fadhley
sumber
2

Membangkitkan kesalahan dari init tidak dapat dihindari dalam beberapa kasus, tetapi melakukan terlalu banyak pekerjaan di init adalah gaya yang buruk. Anda harus mempertimbangkan membuat pabrik atau pseudo-pabrik - metode kelas sederhana yang mengembalikan objek yang diendapkan.

Ctrl-C
sumber