Saya sering melihat komentar pada pertanyaan Stack Overflow lainnya tentang bagaimana penggunaan except: pass
tidak disarankan. Kenapa ini buruk? Kadang-kadang saya hanya tidak peduli apa kesalahannya, dan saya ingin melanjutkan dengan kodenya.
try:
something
except:
pass
Mengapa menggunakan except: pass
blok buruk? Apa yang membuatnya buruk? Apakah ini fakta bahwa saya pass
melakukan kesalahan atau saya except
salah?
logging
modul pada tingkat DEBUG untuk menghindari mereka mengalir keluar dalam produksi, tetapi tetap tersedia dalam pengembangan.Jawaban:
Seperti benar Anda menebak, ada dua sisi untuk itu: Penangkapan setiap kesalahan dengan menetapkan tidak ada jenis terkecuali setelah
except
, dan hanya lewat itu tanpa mengambil tindakan apapun.Penjelasan saya adalah "sedikit" lebih lama — jadi t; dr itu dipecah menjadi ini:
Tapi mari kita masuk ke detail:
Jangan sampai ada kesalahan
Saat menggunakan
try
blok, Anda biasanya melakukan ini karena Anda tahu bahwa ada kemungkinan pengecualian dilemparkan. Dengan demikian, Anda juga sudah memiliki perkiraan tentang apa yang bisa dipatahkan dan pengecualian apa yang bisa dilemparkan. Dalam kasus seperti itu, Anda menangkap pengecualian karena Anda dapat memulihkannya secara positif . Itu berarti bahwa Anda siap untuk pengecualian dan memiliki beberapa rencana alternatif yang akan Anda ikuti jika pengecualian itu.Misalnya, ketika Anda meminta pengguna untuk memasukkan nomor, Anda dapat mengonversi input yang menggunakan
int()
yang dapat meningkatkan aValueError
. Anda dapat dengan mudah memulihkannya dengan hanya meminta pengguna untuk mencobanya lagi, jadi menangkapValueError
dan mendorong pengguna lagi akan menjadi rencana yang tepat. Contoh lain adalah jika Anda ingin membaca beberapa konfigurasi dari file, dan file itu kebetulan tidak ada. Karena ini adalah file konfigurasi, Anda mungkin memiliki beberapa konfigurasi default sebagai cadangan, sehingga file tersebut tidak benar-benar diperlukan. Jadi menangkapFileNotFoundError
dan hanya menerapkan konfigurasi default akan menjadi rencana yang bagus di sini. Sekarang dalam kedua kasus ini, kami memiliki pengecualian yang sangat spesifik yang kami harapkan dan memiliki rencana yang sama spesifik untuk pulih darinya. Dengan demikian, dalam setiap kasus, kami hanya secara eksplisitexcept
tertentu pengecualian.Namun, jika kita ingin menangkap semuanya , maka — selain pengecualian-pengecualian itu kita siap untuk pulih dari — ada juga kemungkinan bahwa kita mendapatkan pengecualian yang tidak kita harapkan, dan yang memang tidak bisa kita pulihkan dari; atau tidak seharusnya pulih dari.
Mari kita ambil contoh file konfigurasi dari atas. Seandainya ada file yang hilang, kami hanya menerapkan konfigurasi default kami, dan mungkin memutuskan di lain waktu untuk secara otomatis menyimpan konfigurasi (jadi lain kali, file itu ada). Sekarang bayangkan kita mendapat
IsADirectoryError
, atau aPermissionError
sebagai gantinya. Dalam kasus seperti itu, kami mungkin tidak ingin melanjutkan; kami masih bisa menerapkan konfigurasi default kami, tetapi kami nanti tidak akan dapat menyimpan file. Dan sepertinya pengguna juga memiliki konfigurasi khusus, jadi menggunakan nilai default kemungkinan tidak diinginkan. Jadi kami ingin memberi tahu pengguna tentang hal itu segera, dan mungkin membatalkan eksekusi program juga. Tapi itu bukan sesuatu yang ingin kita lakukan di suatu tempat jauh di dalam beberapa bagian kode kecil; ini adalah sesuatu yang penting di level aplikasi, jadi itu harus ditangani di atas — jadi biarkan pengecualian muncul.Contoh sederhana lain juga disebutkan dalam dokumen idiom Python 2 . Di sini, salah ketik sederhana ada dalam kode yang menyebabkannya rusak. Karena kami menangkap setiap pengecualian, kami juga menangkap
NameError
s danSyntaxError
s . Keduanya adalah kesalahan yang terjadi pada kita semua saat pemrograman; dan keduanya adalah kesalahan yang sama sekali tidak ingin kami sertakan saat mengirimkan kode. Tetapi karena kami juga menangkapnya, kami bahkan tidak tahu bahwa itu terjadi di sana dan kehilangan bantuan untuk men-debug dengan benar.Tetapi ada juga pengecualian yang lebih berbahaya yang tidak mungkin kita siapkan. Misalnya SystemError biasanya sesuatu yang jarang terjadi dan yang tidak bisa kita rencanakan; itu berarti ada sesuatu yang lebih rumit sedang terjadi, sesuatu yang kemungkinan mencegah kita melanjutkan tugas saat ini.
Bagaimanapun, sangat tidak mungkin bahwa Anda siap untuk semuanya dalam bagian skala kecil dari kode, jadi di situlah Anda hanya perlu menangkap pengecualian yang telah Anda siapkan. Beberapa orang menyarankan untuk menangkap setidaknya
Exception
karena tidak akan mencakup hal-hal sepertiSystemExit
danKeyboardInterrupt
yang dengan desain untuk menghentikan aplikasi Anda, tetapi saya berpendapat bahwa ini masih terlalu spesifik. Hanya ada satu tempat di mana saya secara pribadi menerima penangkapanException
atau sembarang tempatpengecualian, dan itu ada dalam penangan pengecualian tingkat aplikasi global tunggal yang memiliki tujuan tunggal untuk mencatat pengecualian apa pun yang tidak kami persiapkan. Dengan begitu, kita masih dapat menyimpan sebanyak mungkin informasi tentang pengecualian yang tidak terduga, yang kemudian dapat kita gunakan untuk memperluas kode kita untuk menangani kode-kode itu secara eksplisit (jika kita dapat memulihkannya) atau — jika ada bug — untuk membuat kasus uji untuk memastikan itu tidak akan terjadi lagi. Tapi tentu saja, itu hanya berfungsi jika kita hanya menangkap pengecualian yang sudah kita harapkan, jadi yang tidak kita harapkan akan muncul secara alami.Cobalah untuk menghindari lewat kecuali blok
Ketika secara eksplisit menangkap sejumlah kecil pengecualian khusus, ada banyak situasi di mana kita akan baik-baik saja dengan tidak melakukan apa-apa. Dalam kasus seperti itu, memiliki
except SomeSpecificException: pass
saja tidak apa-apa Namun, sebagian besar waktu, ini tidak terjadi karena kita mungkin memerlukan beberapa kode yang berkaitan dengan proses pemulihan (seperti yang disebutkan di atas). Ini bisa berupa sesuatu yang mencoba lagi tindakan, atau untuk menetapkan nilai default sebagai gantinya.Jika bukan itu masalahnya, misalnya karena kode kami sudah terstruktur untuk diulangi sampai berhasil, maka hanya lewat saja sudah cukup. Mengambil contoh kami dari atas, kami mungkin ingin meminta pengguna untuk memasukkan nomor. Karena kami tahu bahwa pengguna ingin tidak melakukan apa yang kami minta, kami mungkin hanya memasukkannya ke dalam lingkaran, sehingga bisa terlihat seperti ini:
Karena kami terus berusaha sampai tidak ada pengecualian yang dilemparkan, kami tidak perlu melakukan sesuatu yang istimewa di blok kecuali, jadi ini baik-baik saja. Tapi tentu saja, orang mungkin berargumen bahwa kami setidaknya ingin menunjukkan kepada pengguna beberapa pesan kesalahan untuk memberi tahu mengapa ia harus mengulangi input.
Namun dalam banyak kasus lain, hanya lewat dalam
except
adalah tanda bahwa kita tidak benar-benar siap untuk pengecualian yang kita tangkap. Kecuali jika pengecualian itu sederhana (sukaValueError
atau tidakTypeError
), dan alasan mengapa kita bisa lulus sudah jelas, cobalah untuk tidak hanya lewat. Jika benar-benar tidak ada yang dapat dilakukan (dan Anda benar-benar yakin tentang hal itu), maka pertimbangkan untuk menambahkan komentar mengapa demikian; jika tidak, perluas blok kecuali untuk benar-benar memasukkan beberapa kode pemulihan.except: pass
Pelaku terburuk adalah kombinasi keduanya. Ini berarti bahwa kita rela menangkap kesalahan apa pun meskipun kita sama sekali tidak siap untuk itu dan kita juga tidak melakukan apa-apa. Anda setidaknya ingin mencatat kesalahan dan juga kemungkinan mengembalikannya untuk tetap menghentikan aplikasi (sepertinya Anda tidak dapat melanjutkan seperti biasa setelah MemoryError). Melewati saja tidak hanya akan membuat aplikasi tetap hidup (tergantung di mana Anda menangkap tentu saja), tetapi juga membuang semua informasi, sehingga tidak mungkin untuk menemukan kesalahan — yang terutama benar jika Anda bukan orang yang menemukannya.
Jadi intinya adalah: Hanya menangkap pengecualian yang benar-benar Anda harapkan dan siap untuk pulih dari; semua yang lain kemungkinan adalah kesalahan yang harus Anda perbaiki, atau sesuatu yang Anda tidak siap untuk melakukannya. Melewati pengecualian spesifik tidak masalah jika Anda benar-benar tidak perlu melakukan sesuatu tentang hal itu. Dalam semua kasus lain, itu hanya tanda anggapan dan malas. Dan Anda pasti ingin memperbaikinya.
sumber
except
, tetapi kemudian memanggilraise
tanpa argumen untuk terus membiarkan pengecualian muncul, mengakhiri aplikasi. Saya menyukainya: ianbicking.org/blog/2007/09/re-raising-exceptions.html . Sepertinya pengecualian kuat untuk aturan tentang tidak menggunakan selimutexcept
.raise
. Anda biasanya hanya melakukan ini di beberapa lokasi dalam aplikasi Anda untuk mencatat pengecualian.Masalah utama di sini adalah mengabaikan semua kesalahan: Kehabisan memori, CPU menyala, pengguna ingin berhenti, program ingin keluar, Jabberwocky membunuh pengguna.
Ini terlalu banyak. Di kepala Anda, Anda berpikir "Saya ingin mengabaikan kesalahan jaringan ini". Jika terjadi sesuatu yang tidak terduga , maka kode Anda diam-diam melanjutkan dan memecah dengan cara yang benar-benar tidak dapat diprediksi sehingga tidak ada yang dapat melakukan debug.
Itu sebabnya Anda harus membatasi diri untuk mengabaikan hanya beberapa kesalahan tertentu dan membiarkan sisanya berlalu.
sumber
Menjalankan kode pseudo Anda secara harfiah bahkan tidak memberikan kesalahan:
seolah-olah itu adalah potongan kode yang benar-benar valid, bukannya melempar a
NameError
. Saya harap ini bukan yang Anda inginkan.sumber
Ini menangkap setiap kemungkinan pengecualian, termasuk
GeneratorExit
,KeyboardInterrupt
, danSystemExit
- yang pengecualian Anda mungkin tidak berniat untuk menangkap. Itu sama dengan menangkapBaseException
.Dokumentasi versi lama mengatakan :
Hirarki Pengecualian Python
Jika Anda menangkap kelas pengecualian orang tua, Anda juga menangkap semua kelas anak mereka. Jauh lebih elegan jika hanya menangkap pengecualian yang siap Anda tangani.
Inilah hierarki pengecualian Python 3 - apakah Anda benar-benar ingin menangkap mereka semua ?:
Jangan Lakukan ini
Jika Anda menggunakan bentuk penanganan pengecualian ini:
Maka Anda tidak akan dapat mengganggu
something
blok Anda dengan Ctrl-C. Program Anda akan mengabaikan setiap Pengecualian yang mungkin di dalamtry
blok kode.Berikut ini contoh lain yang akan memiliki perilaku yang tidak diinginkan yang sama:
Alih-alih, cobalah hanya menangkap pengecualian spesifik yang Anda tahu sedang Anda cari. Misalnya, jika Anda tahu Anda mungkin mendapatkan kesalahan nilai pada konversi:
Penjelasan lebih lanjut dengan contoh lain
Anda mungkin melakukannya karena Anda telah mengikis web dan mengatakan, a
UnicodeError
, tetapi karena Anda telah menggunakan penangkapan Pengecualian terluas, kode Anda, yang mungkin memiliki kelemahan mendasar lainnya, akan berusaha berjalan hingga selesai, menghabiskan bandwidth , waktu pemrosesan, keausan pada peralatan Anda, kehabisan memori, mengumpulkan data sampah, dll.Jika orang lain meminta Anda untuk menyelesaikan sehingga mereka dapat mengandalkan kode Anda, saya mengerti merasa terdorong untuk hanya menangani semuanya. Tetapi jika Anda ingin gagal dengan ribut saat Anda berkembang, Anda akan memiliki kesempatan untuk memperbaiki masalah yang mungkin hanya muncul sebentar-sebentar, tetapi itu akan menjadi bug jangka panjang yang mahal.
Dengan penanganan kesalahan yang lebih tepat, kode Anda bisa lebih kuat.
sumber
Jadi, inilah pendapat saya. Setiap kali Anda menemukan kesalahan, Anda harus melakukan sesuatu untuk mengatasinya, yaitu menulisnya di logfile atau sesuatu yang lain. Setidaknya, ini memberi tahu Anda bahwa dulu ada kesalahan.
sumber
Anda harus menggunakan setidaknya
except Exception:
untuk menghindari menangkap pengecualian sistem sepertiSystemExit
atauKeyboardInterrupt
. Berikut tautan ke dokumen.Secara umum Anda harus mendefinisikan secara eksplisit pengecualian yang ingin Anda tangkap, untuk menghindari penangkapan pengecualian yang tidak diinginkan. Anda harus tahu pengecualian apa yang Anda abaikan .
sumber
Pertama, itu melanggar dua prinsip Zen Python :
Artinya, Anda sengaja membuat kesalahan Anda diam-diam. Selain itu, Anda tidak tahu peristiwa, kesalahan mana yang sebenarnya terjadi, karena
except: pass
akan menangkap pengecualian.Kedua, jika kami mencoba untuk abstrak dari Zen Python, dan berbicara dengan kewarasan, Anda harus tahu, bahwa menggunakan
except:pass
membuat Anda tidak memiliki pengetahuan dan kontrol dalam sistem Anda. Aturan praktisnya adalah untuk meningkatkan pengecualian, jika kesalahan terjadi, dan mengambil tindakan yang sesuai. Jika Anda tidak tahu sebelumnya, tindakan apa yang harus dilakukan, setidaknya catat kesalahan di suatu tempat (dan lebih baik kembalikan pengecualian):Tapi, biasanya, jika Anda mencoba menangkap pengecualian, Anda mungkin melakukan kesalahan!
sumber
The
except:pass
konstruk dasarnya membungkam kondisi luar biasa setiap dan semua yang muncul sedangkan kode tercakup dalamtry:
blok yang sedang dijalankan.Apa yang membuat praktik buruk ini adalah bahwa itu biasanya bukan yang Anda inginkan. Lebih sering, beberapa kondisi spesifik muncul yang Anda ingin hening, dan
except:pass
terlalu banyak instrumen tumpul. Ini akan menyelesaikan pekerjaan, tetapi juga akan menutupi kondisi kesalahan lain yang mungkin belum Anda antisipasi, tetapi mungkin ingin ditangani dengan cara lain.Apa yang membuat ini sangat penting dalam Python adalah bahwa dengan idiom bahasa ini, pengecualian tidak selalu kesalahan . Mereka sering digunakan dengan cara ini, tentu saja, seperti dalam kebanyakan bahasa. Tapi Python secara khusus kadang-kadang menggunakannya untuk mengimplementasikan jalur keluar alternatif dari beberapa tugas kode yang tidak benar-benar bagian dari kasus berjalan normal, tetapi masih diketahui muncul dari waktu ke waktu dan bahkan mungkin diharapkan dalam kebanyakan kasus.
SystemExit
telah disebutkan sebagai contoh lama, tetapi contoh paling umum saat ini mungkinStopIteration
. Menggunakan pengecualian dengan cara ini menyebabkan banyak kontroversi, terutama ketika iterator dan generator pertama kali diperkenalkan ke Python, tetapi akhirnya gagasan itu menang.sumber
Alasan # 1 telah dinyatakan - menyembunyikan kesalahan yang tidak Anda harapkan.
(# 2) - Ini membuat kode Anda sulit dibaca dan dipahami orang lain. Jika Anda menangkap FileNotFoundException ketika Anda mencoba membaca file, maka cukup jelas bagi pengembang lain apa fungsi yang harus dimiliki oleh blok 'catch'. Jika Anda tidak menentukan pengecualian, maka Anda perlu komentar tambahan untuk menjelaskan apa yang harus dilakukan oleh blok.
(# 3) - Ini menunjukkan pemrograman malas. Jika Anda menggunakan try / catch generik, ini menunjukkan bahwa Anda tidak memahami kemungkinan kesalahan run-time dalam program Anda, atau bahwa Anda tidak tahu pengecualian apa yang dimungkinkan dalam Python. Menangkap kesalahan spesifik menunjukkan bahwa Anda memahami program dan rentang kesalahan yang dilemparkan Python. Ini lebih cenderung membuat pengembang lain dan peninjau kode mempercayai pekerjaan Anda.
sumber
Jadi, output apa yang dihasilkan kode ini?
Sekarang bayangkan
try
-except
blok adalah ratusan baris panggilan ke hierarki objek yang kompleks, dan itu sendiri disebut di tengah pohon panggilan program besar. Ketika programnya salah, di mana Anda mulai mencari?sumber
Secara umum, Anda dapat mengklasifikasikan kesalahan / pengecualian dalam salah satu dari tiga kategori :
Fatal : Bukan salahmu, kamu tidak bisa mencegahnya, kamu tidak bisa pulih darinya. Anda tentu tidak boleh mengabaikannya dan melanjutkan, dan meninggalkan program Anda dalam keadaan yang tidak diketahui. Biarkan saja kesalahan menghentikan program Anda, tidak ada yang dapat Anda lakukan.
Boneheaded : Kesalahan Anda sendiri, kemungkinan besar karena kesalahan pengawasan, bug, atau pemrograman. Anda harus memperbaiki bug. Sekali lagi, Anda tentu tidak boleh mengabaikan dan melanjutkan.
Eksogen : Anda dapat mengharapkan kesalahan ini dalam situasi luar biasa, seperti file tidak ditemukan atau koneksi terputus . Anda harus secara eksplisit menangani kesalahan ini, dan hanya ini.
Dalam semua kasus
except: pass
hanya akan meninggalkan program Anda dalam keadaan yang tidak diketahui, di mana ia dapat menyebabkan lebih banyak kerusakan.sumber
Sederhananya, jika pengecualian atau kesalahan dilemparkan, ada sesuatu yang salah. Ini mungkin bukan sesuatu yang sangat salah, tetapi membuat, melempar, dan menangkap kesalahan dan pengecualian hanya demi menggunakan pernyataan goto bukanlah ide yang baik, dan itu jarang dilakukan. 99% dari waktu, ada masalah di suatu tempat.
Masalah perlu diatasi. Seperti halnya dalam kehidupan, dalam pemrograman, jika Anda membiarkan masalah sendirian dan mencoba mengabaikannya, masalah itu tidak hilang begitu saja; alih-alih mereka menjadi lebih besar dan berlipat ganda. Untuk mencegah masalah tumbuh pada Anda dan menyerang lagi lebih jauh di jalan, Anda 1) menghilangkannya dan membersihkan kekacauan sesudahnya, atau 2) mengatasinya dan membersihkan kekacauan sesudahnya.
Mengabaikan pengecualian dan kesalahan dan membiarkannya seperti itu adalah cara yang baik untuk mengalami kebocoran memori, koneksi basis data yang luar biasa, kunci yang tidak perlu pada izin file, dll.
Pada kesempatan yang jarang, masalahnya sangat kecil, sepele, dan - selain perlu dicoba ... catch block - mandiri , sehingga benar-benar tidak ada kekacauan yang harus dibersihkan setelahnya. Ini adalah satu-satunya kesempatan ketika praktik terbaik ini tidak selalu berlaku. Dalam pengalaman saya, ini secara umum berarti bahwa apa pun yang dilakukan kode pada dasarnya kecil dan tidak dapat dilupakan, dan sesuatu seperti coba lagi upaya atau pesan khusus tidak sebanding dengan kompleksitas maupun menahan utas.
Di perusahaan saya, aturannya adalah hampir selalu melakukan sesuatu di blok penangkap, dan jika Anda tidak melakukan apa pun, maka Anda harus selalu memberikan komentar dengan alasan yang sangat bagus mengapa tidak. Anda tidak boleh melewati atau meninggalkan blok penangkap kosong ketika ada sesuatu yang harus dilakukan.
sumber
Menurut pendapat saya kesalahan memiliki alasan untuk muncul, bahwa suara saya bodoh, tetapi memang begitulah adanya. Pemrograman yang baik hanya memunculkan kesalahan saat Anda harus menanganinya. Juga, seperti yang saya baca beberapa waktu lalu, "pass-Statement adalah Pernyataan yang menunjukkan kode yang akan dimasukkan nanti", jadi jika Anda ingin memiliki pernyataan-kecuali yang kosong jangan ragu untuk melakukannya, tetapi untuk program yang bagus akan ada menjadi bagian yang hilang. karena Anda tidak menangani hal-hal yang seharusnya Anda miliki. Munculnya pengecualian memberi Anda kesempatan untuk memperbaiki data input atau mengubah struktur data Anda sehingga pengecualian ini tidak terjadi lagi (tetapi dalam kebanyakan kasus (Pengecualian jaringan, Pengecualian masukan umum) pengecualian menunjukkan bahwa bagian selanjutnya dari program tidak akan berjalan dengan baik. Misalnya NetworkException dapat menunjukkan koneksi jaringan yang rusak dan program tidak dapat mengirim / menerima data pada langkah program selanjutnya.
Tetapi menggunakan pass block untuk hanya satu blok eksekusi adalah valid, karena Anda masih membedakan antara tipe pengecualian, jadi jika Anda meletakkan semua blok pengecualian dalam satu, itu tidak kosong:
dapat ditulis ulang seperti itu:
Jadi, bahkan beberapa blok kecuali dengan pernyataan lulus dapat menghasilkan kode, yang strukturnya menangani jenis pengecualian khusus.
sumber
Semua komentar yang diajukan sejauh ini valid. Jika memungkinkan, Anda perlu menentukan pengecualian apa yang ingin Anda abaikan. Jika memungkinkan, Anda perlu menganalisis apa yang menyebabkan pengecualian, dan hanya mengabaikan apa yang ingin Anda abaikan, dan bukan sisanya. Jika pengecualian menyebabkan aplikasi "macet secara spektakuler", maka itulah, karena jauh lebih penting untuk mengetahui hal yang tidak terduga terjadi ketika itu terjadi, daripada menyembunyikan bahwa masalahnya pernah terjadi.
Dengan semua yang dikatakan, jangan mengambil praktik pemrograman sebagai yang terpenting. Ini bodoh. Selalu ada waktu dan tempat untuk melakukan blok abaikan-semua-pengecualian.
Contoh penting lainnya yang idiot adalah penggunaan
goto
operator. Ketika saya masih di sekolah, profesor kami mengajari kamigoto
operator hanya untuk menyebutkan bahwa Anda tidak boleh menggunakannya, PERNAH. Jangan percaya orang mengatakan kepada Anda bahwa xyz seharusnya tidak pernah digunakan dan tidak mungkin ada skenario ketika itu berguna. Selalu ada.sumber
Menangani kesalahan sangat penting dalam pemrograman. Anda harus menunjukkan kepada pengguna apa yang salah. Dalam beberapa kasus Anda dapat mengabaikan kesalahan. Ini adalah praktik pemrograman yang sangat buruk.
sumber
Karena belum disebutkan, itu gaya yang lebih baik untuk digunakan
contextlib.suppress
:Perhatikan bahwa dalam contoh yang diberikan, status program tetap sama, terlepas dari tidaknya pengecualian terjadi. Artinya,
somefile.tmp
selalu menjadi tidak ada.sumber