Apa cara yang tepat untuk mendeklarasikan kelas pengecualian khusus dalam Python modern? Tujuan utama saya adalah mengikuti standar apa pun yang dimiliki kelas pengecualian lain, sehingga (misalnya) string tambahan apa pun yang saya sertakan dalam pengecualian dicetak oleh alat apa pun yang menangkap pengecualian.
Dengan "Python modern" Maksud saya sesuatu yang akan berjalan dalam Python 2.5 tetapi menjadi 'benar' untuk Python 2.6 dan Python 3. * cara melakukan sesuatu. Dan dengan "kebiasaan" yang saya maksud adalah objek Pengecualian yang dapat menyertakan data tambahan tentang penyebab kesalahan: string, mungkin juga beberapa objek arbitrer lain yang relevan dengan pengecualian.
Saya tersandung oleh peringatan penghentian berikut di Python 2.6.2:
>>> class MyError(Exception):
... def __init__(self, message):
... self.message = message
...
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
Tampaknya gila yang BaseException
memiliki makna khusus untuk atribut yang dinamai message
. Saya kumpulkan dari PEP-352 bahwa atribut memang memiliki arti khusus dalam 2.5 yang mereka coba hilangkan, jadi saya kira nama itu (dan itu saja) sekarang dilarang? Ugh.
Saya juga tidak sadar yang Exception
memiliki beberapa parameter ajaib args
, tetapi saya tidak pernah tahu cara menggunakannya. Saya juga tidak yakin itu cara yang tepat untuk melakukan berbagai hal ke depan; banyak diskusi yang saya temukan online menyarankan mereka mencoba untuk menghilangkan args dengan Python 3.
Pembaruan: dua jawaban telah menyarankan penggantian __init__
, dan __str__
/ __unicode__
/ __repr__
. Sepertinya banyak mengetik, apakah perlu?
sumber
Dengan Pengecualian Python modern, Anda tidak perlu penyalahgunaan
.message
, atau menimpa.__str__()
atau.__repr__()
atau apapun itu. Jika semua yang Anda inginkan adalah pesan informatif ketika pengecualian Anda muncul, lakukan ini:Itu akan memberikan traceback yang diakhiri dengan
MyException: My hovercraft is full of eels
.Jika Anda menginginkan lebih banyak fleksibilitas dari pengecualian, Anda bisa meneruskan kamus sebagai argumen:
Namun, untuk mendapatkan rincian itu dalam sebuah
except
blok sedikit lebih rumit. Rinciannya disimpan dalamargs
atribut, yang merupakan daftar. Anda perlu melakukan sesuatu seperti ini:Masih dimungkinkan untuk mengirimkan beberapa item ke pengecualian dan mengaksesnya melalui indeks tuple, tetapi ini sangat tidak disarankan (dan bahkan dimaksudkan untuk penghentian beberapa waktu yang lalu). Jika Anda membutuhkan lebih dari satu informasi dan metode di atas tidak cukup untuk Anda, maka Anda harus subkelas
Exception
seperti yang dijelaskan dalam tutorial .sumber
Exception(foo, bar, qux)
.Ini baik-baik saja, kecuali pengecualian Anda benar-benar jenis pengecualian yang lebih spesifik:
Atau lebih baik (mungkin sempurna), daripada
pass
memberikan docstring:Subkelas Pengecualian Subkelas
Dari dokumen
Itu berarti bahwa jika pengecualian Anda adalah jenis pengecualian yang lebih spesifik, subkelas pengecualian itu daripada generik
Exception
(dan hasilnya adalah Anda masih berasal dariException
seperti yang direkomendasikan oleh dokumen). Juga, Anda setidaknya dapat memberikan dokumen (dan tidak dipaksa untuk menggunakanpass
kata kunci):Tetapkan atribut yang Anda buat sendiri dengan kustom
__init__
. Hindari melewatkan dikt sebagai argumen posisional, pengguna masa depan kode Anda akan berterima kasih. Jika Anda menggunakan atribut pesan yang tidak digunakan lagi, menetapkannya sendiri akan menghindariDeprecationWarning
:Benar-benar tidak perlu menulis sendiri
__str__
atau__repr__
. Yang builtin sangat bagus, dan warisan koperasi Anda memastikan bahwa Anda menggunakannya.Kritik atas jawaban teratas
Sekali lagi, masalah dengan hal di atas adalah bahwa untuk menangkapnya, Anda harus menyebutkannya secara khusus (mengimpornya jika dibuat di tempat lain) atau menangkap Pengecualian, (tetapi Anda mungkin tidak siap untuk menangani semua jenis Pengecualian, dan Anda seharusnya hanya menangkap pengecualian yang siap Anda tangani). Kritik serupa dengan yang di bawah ini, tetapi juga bukan cara untuk menginisialisasi melalui
super
, dan Anda akan mendapatkanDeprecationWarning
jika Anda mengakses atribut pesan:Ini juga membutuhkan tepat dua argumen untuk diteruskan (selain dari
self
.) Tidak lebih, tidak kurang. Itu adalah kendala yang menarik yang mungkin tidak dihargai oleh pengguna di masa depan.Untuk menjadi langsung - itu melanggar substitusi Liskov .
Saya akan menunjukkan kedua kesalahan:
Dibandingkan dengan:
sumber
BaseException.message
hilang dalam Python 3, jadi kritiknya hanya berlaku untuk versi lama, kan?__str__
metodeMyAppValueError
alih-alih mengandalkanmessage
atributValueError
. Ini masuk akal jika itu dalam kategori Kesalahan Nilai. Jika tidak dalam kategori Kesalahan Nilai, saya akan membantahnya pada semantik. Ada ruang untuk beberapa nuansa dan alasan di bagian programmer, tapi saya lebih suka kekhususan bila berlaku. Saya akan memperbarui jawaban saya untuk lebih baik menangani masalah ini dalam waktu dekat.lihat bagaimana pengecualian berfungsi secara default jika satu vs lebih banyak atribut digunakan (tracebacks dihilangkan):
jadi Anda mungkin ingin memiliki semacam " template pengecualian ", yang berfungsi sebagai pengecualian itu sendiri, dengan cara yang kompatibel:
ini dapat dilakukan dengan mudah dengan subkelas ini
dan jika Anda tidak menyukai representasi standar seperti tuple, cukup tambahkan
__str__
metode keExceptionTemplate
kelas, seperti:dan kamu akan punya
sumber
Pada Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html ), metode yang disarankan masih:
Tolong jangan lupa untuk mendokumentasikan, mengapa pengecualian khusus diperlukan?
Jika perlu, inilah cara untuk pengecualian dengan lebih banyak data:
dan ambil mereka seperti:
payload=None
Penting untuk membuatnya menjadi acar. Sebelum membuangnya, Anda harus meneleponerror.__reduce__()
. Memuat akan berfungsi seperti yang diharapkan.Anda mungkin harus menyelidiki dalam menemukan solusi menggunakan
return
pernyataan ular sanca jika Anda memerlukan banyak data untuk ditransfer ke beberapa struktur luar. Ini tampaknya lebih jelas / lebih pythonic bagi saya. Pengecualian tingkat lanjut banyak digunakan di Jawa, yang kadang-kadang bisa menjengkelkan, saat menggunakan kerangka kerja dan harus menangkap semua kemungkinan kesalahan.sumber
__str__
) daripada jawaban lain yang menggunakansuper().__init__(...)
.. Hanya rasa malu yang menimpa__str__
dan__repr__
mungkin diperlukan hanya untuk serialisasi "default" yang lebih baik.Anda harus mengganti
__repr__
atau__unicode__
metode alih-alih menggunakan pesan, argumen yang Anda berikan saat Anda membuat pengecualian akan berada dalamargs
atribut objek pengecualian.sumber
Tidak, "pesan" tidak dilarang. Itu sudah usang. Aplikasi Anda akan berfungsi dengan baik dengan menggunakan pesan. Tapi Anda mungkin ingin menyingkirkan kesalahan penghinaan, tentu saja.
Saat Anda membuat kelas pengecualian khusus untuk aplikasi Anda, banyak dari mereka tidak subkelas hanya dari pengecualian, tetapi dari yang lain, seperti ValueError atau serupa. Maka Anda harus beradaptasi dengan penggunaan variabel mereka.
Dan jika Anda memiliki banyak pengecualian dalam aplikasi Anda, biasanya ide yang baik untuk memiliki kelas dasar kustom umum untuk semuanya, sehingga pengguna modul Anda dapat melakukan
Dan dalam hal ini Anda dapat melakukan
__init__ and __str__
dibutuhkan di sana, sehingga Anda tidak perlu mengulanginya untuk setiap pengecualian. Tetapi hanya memanggil variabel pesan itu sesuatu yang lain dari pada pesanlah yang berhasil.Bagaimanapun, Anda hanya perlu
__init__ or __str__
jika Anda melakukan sesuatu yang berbeda dari apa yang dilakukan oleh Pengecualian itu sendiri. Dan karena jika penghentian, Anda kemudian membutuhkan keduanya, atau Anda mendapatkan kesalahan. Itu tidak banyak kode tambahan yang Anda butuhkan per kelas. ;)sumber
Lihat artikel yang sangat bagus " Panduan definitif untuk pengecualian Python ". Prinsip dasarnya adalah:
BaseException.__init__
dengan hanya satu argumen.Ada juga informasi tentang pengorganisasian (dalam modul) dan pengecualian pembungkus, saya sarankan untuk membaca panduan ini.
sumber
Always call BaseException.__init__ with only one argument.
Sepertinya kendala yang tidak dibutuhkan, karena sebenarnya menerima sejumlah argumen.Coba Contoh ini
sumber
Untuk menetapkan pengecualian Anda dengan benar, ada beberapa praktik terbaik yang perlu Anda ikuti:
Tentukan kelas dasar yang diwarisi dari
Exception
. Ini akan memungkinkan untuk menangkap pengecualian yang terkait dengan proyek (pengecualian yang lebih spesifik harus diwarisi darinya):Mengatur kelas pengecualian ini dalam modul terpisah (mis.
exceptions.py
) Umumnya merupakan ide yang bagus.Untuk membuat pengecualian khusus, subkelas kelas pengecualian dasar.
Untuk menambahkan dukungan untuk argumen tambahan ke pengecualian khusus, tentukan
__init__()
metode kustom dengan sejumlah variabel argumen. Panggil kelas dasar__init__()
, serahkan argumen posisi apa pun ke sana (ingat bahwaBaseException
/Exception
harapkan sejumlah argumen posisional ):Untuk mengajukan pengecualian dengan argumen tambahan, Anda dapat menggunakan:
Desain ini mematuhi prinsip substitusi Liskov , karena Anda dapat mengganti instance kelas pengecualian dasar dengan instance kelas pengecualian turunan. Juga, ini memungkinkan Anda untuk membuat turunan dari kelas turunan dengan parameter yang sama dengan induknya.
sumber