Apakah saya harus selalu menetapkan jenis pengecualian dalam pernyataan `kecuali`?

93

Saat menggunakan PyCharm IDE, penggunaan except:tanpa jenis pengecualian memicu pengingat dari IDE bahwa klausa pengecualian ini adalah Too broad.

Haruskah saya mengabaikan nasihat ini? Atau apakah Pythonic selalu menentukan jenis pengecualian?

HorseloverFat
sumber
Jika Anda ingin mengetahui lebih banyak tentang ini daripada jawabannya, google menelan pengecualian. Anda dapat melakukan segala macam hal yang menarik dan tidak boleh dilakukan bersama mereka. Bau kode adalah satu lagi.
Tony Hopkinson

Jawaban:

89

Hampir selalu lebih baik untuk menentukan jenis pengecualian eksplisit. Jika Anda menggunakan except:klausa telanjang , Anda mungkin akan menemukan pengecualian selain yang Anda harapkan untuk ditangkap - ini dapat menyembunyikan bug atau mempersulit program debug ketika mereka tidak melakukan apa yang Anda harapkan.

Misalnya, jika Anda menyisipkan baris ke dalam database, Anda mungkin ingin menangkap pengecualian yang menunjukkan bahwa baris tersebut sudah ada, sehingga Anda bisa melakukan pembaruan.

try:
    insert(connection, data)
except:
    update(connection, data)

Jika Anda menentukan telanjang except:, Anda juga akan menemukan kesalahan soket yang menunjukkan bahwa server database telah jatuh. Sebaiknya hanya menangkap pengecualian yang Anda tahu cara menanganinya - seringkali lebih baik jika program gagal pada titik pengecualian daripada melanjutkan tetapi berperilaku aneh dan tidak terduga.

Satu kasus di mana Anda mungkin ingin menggunakan telanjang except:adalah di tingkat atas program yang Anda butuhkan untuk selalu berjalan, seperti server jaringan. Tapi kemudian, Anda harus sangat berhati-hati untuk mencatat pengecualian, jika tidak, tidak mungkin mengetahui apa yang salah. Pada dasarnya, hanya boleh ada paling banyak satu tempat dalam program yang melakukan ini.

Konsekuensi dari semua ini adalah bahwa kode Anda tidak boleh melakukannya raise Exception('some message')karena memaksa kode klien untuk digunakan except:(atau except Exception:yang hampir sama buruknya). Anda harus menentukan pengecualian khusus untuk masalah yang ingin Anda beri sinyal (mungkin mewarisi dari beberapa subkelas pengecualian bawaan seperti ValueErroratau TypeError). Atau Anda harus meningkatkan pengecualian bawaan tertentu. Ini memungkinkan pengguna kode Anda untuk berhati-hati dalam menangkap hanya pengecualian yang ingin mereka tangani.

babbageclunk
sumber
7
+1 Sangat benar. Bahkan lebih menyenangkan dengan contoh: except:juga menangkap (di antara banyak hal lainnya) NameErrordan AttributeError, jadi jika Anda salah mengeja sesuatu di tryblok (misalnya fungsi "sisipkan" Anda sebenarnya dipanggil insert_onekarena seseorang tidak menghargai konsistensi sebanyak yang seharusnya), itu selalu diam-diam mencoba update().
1
Jadi bagaimana dengan saat Anda perlu memastikan pengecualian tidak dilemparkan di atas situs panggilan saat ini? Yaitu - Saya telah menangkap semua pengecualian spesifik yang dapat saya perkirakan akan terlempar, sekarang saya perlu menambahkan bahwa "jika hal yang belum saya pikirkan ini terlempar, saya perlu mencatatnya sebelum membunuh konteks eksekusi yang berjalan" (seperti main())?
Adam Parkin
Itulah jenis situasi yang saya bicarakan di paragraf awal 'Satu kasus ...'. Terkadang memang diperlukan, tetapi program apa pun harus benar-benar hanya memiliki satu tempat yang melakukan itu. Dan Anda harus berhati-hati agar jelas di log apa yang terjadi, jika tidak Anda akan memiliki waktu frustasi mencoba mencari tahu mengapa program Anda tidak menangani acara / permintaan / apapun dengan benar.
babbageclunk
3
@ Delnan: Lebih buruk dari itu. except Exception:akan menangkap NameErrordan AttributeErrorjuga. Apa yang membuat telanjang except:begitu buruk adalah bahwa ia menangkap hal-hal yang tidak ada urusannya ditangkap, misalnya SystemExit(diangkat saat Anda menelepon exitatau sys.exit, dan sekarang Anda telah mencegah jalan keluar yang diinginkan) dan KeyboardInterrupt(sekali lagi, jika pengguna memukul Ctrl-C, Anda mungkin tidak ingin untuk tetap berlari hanya untuk membencinya). Hanya yang terakhir yang masuk akal untuk ditangkap, dan itu harus ditangkap secara eksplisit. Setidaknya except Exception:biarkan keduanya menyebar secara normal.
ShadowRanger
38

Anda tidak boleh mengabaikan nasihat yang diberikan penerjemah kepada Anda.

Dari Panduan Gaya PEP-8 untuk Python:

Saat menangkap pengecualian, sebutkan pengecualian khusus bila memungkinkan daripada menggunakan klausa kecuali: telanjang.

Misalnya, gunakan:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

A bare exception: clause akan menangkap pengecualian SystemExit dan KeyboardInterrupt, membuatnya lebih sulit untuk mengganggu program dengan Control-C, dan dapat menyamarkan masalah lain. Jika Anda ingin menangkap semua pengecualian yang menandakan kesalahan program, gunakan kecuali Exception: (telanjang kecuali sama dengan kecuali BaseException :).

Aturan praktis yang baik adalah membatasi penggunaan klausa 'kecuali' untuk dua kasus:

Jika penangan pengecualian akan mencetak atau mencatat traceback; setidaknya pengguna akan menyadari bahwa telah terjadi kesalahan. Jika kode perlu melakukan beberapa pekerjaan pembersihan, tetapi kemudian biarkan pengecualian menyebar ke atas dengan kenaikan. coba ... akhirnya bisa menjadi cara yang lebih baik untuk menangani kasus ini.

asheeshr
sumber
9

Ini tidak spesifik untuk Python.

Inti dari pengecualian adalah menangani masalah sedekat mungkin dengan penyebabnya.

Jadi, Anda menyimpan kode yang dapat dalam keadaan luar biasa dapat memicu masalah dan resolusi "bersebelahan" satu sama lain.

Masalahnya adalah Anda tidak bisa mengetahui semua pengecualian yang bisa dilemparkan oleh sepotong kode. Yang dapat Anda ketahui adalah bahwa jika itu adalah pengecualian untuk mengatakan file tidak ditemukan, maka Anda bisa menjebaknya dan meminta pengguna untuk mendapatkan yang tidak atau membatalkan fungsi.

Jika Anda mencoba menangkapnya, maka tidak peduli masalah apa yang ada dalam rutin file Anda (hanya baca, izin, UAC, bukan pdf, dll), setiap orang akan masuk ke file Anda tidak ditemukan, dan pengguna Anda berteriak "tapi itu ada, kode ini omong kosong"

Sekarang ada beberapa situasi di mana Anda mungkin menangkap semuanya, tetapi mereka harus dipilih secara sadar.

Mereka menangkap, membatalkan beberapa tindakan lokal (seperti membuat atau mengunci sumber daya, (membuka file pada disk untuk menulis misalnya), lalu Anda melempar pengecualian lagi, untuk ditangani di tingkat yang lebih tinggi)

Anda yang lain adalah Anda tidak peduli mengapa itu salah. Mencetak misalnya. Anda mungkin memiliki pemahaman bahwa, untuk mengatakan Ada beberapa masalah dengan printer Anda, tolong selesaikan, dan jangan mematikan aplikasi karena itu. Ona sama sia-sia jika kode Anda mengeksekusi serangkaian tugas terpisah menggunakan semacam jadwal, Anda tidak ingin semuanya mati, karena salah satu tugas gagal.

Catatan Jika Anda melakukan hal di atas, saya tidak dapat merekomendasikan semacam pencatatan pengecualian, misalnya coba tangkap log akhir, cukup tinggi.

Tony Hopkinson
sumber
Saya akan mengatakan ini tentang keseimbangan. Anda harus menangkap pengecualian cukup awal untuk dapat memulihkannya dan cukup terlambat untuk mengetahui ke mana harus pergi bersamanya. Itulah mengapa penanganan pengecualian Java menyebabkan malapetaka seperti itu, karena Anda harus membungkus ulang pengecualian di setiap langkah dan Anda kehilangan informasi.
bukit pasir
2
+1 untuk "Anda tidak peduli mengapa itu salah." Saya menggunakannya di beberapa tempat di sekitar satu baris kode tempat saya mengurai tanggal / waktu dari URL. Pustaka penguraian tanggal / waktu pihak ketiga tidak mencantumkan semua pengecualian yang dapat dilontarkannya (saya telah menemukan OverflowError dan TypeError selain ValueError standar, tetapi mungkin ada lebih banyak), dan bagaimanapun saya benar-benar tidak peduli mengapa pengecualian dilempar, saya hanya ingin memberikan pesan kesalahan yang masuk akal kembali kepada pengguna yang mengatakan bahwa ada yang salah dengan tanggal / waktu.
Michael Rodby
3

Anda juga akan menangkap misalnya Control-C dengan itu, jadi jangan lakukan kecuali Anda "membuang" lagi. Namun, dalam hal ini Anda sebaiknya menggunakan "akhirnya".

Ulrich Eckhardt
sumber
3

Selalu menentukan jenis pengecualian, ada banyak jenis Anda tidak ingin menangkap, seperti SyntaxError, KeyboardInterrupt, MemoryErrordll

Pavel Anossov
sumber
2
akan menggunakan except Exception:menghindari jenis di atas yang tidak ingin kita tangkap?
HorseloverFat
except Exceptionbaik-baik saja.
Ulrich Eckhardt
4
@HorseloverFat: except Exceptionmenangkap SyntaxErrordan MemoryErrorkarena itu adalah kelas dasar mereka. KeyboardInterrupt, SystemExit(dimunculkan oleh sys.exit()) tidak tertangkap (mereka adalah subclass BaseException langsung)
jfs
terdengar seperti itu tidak ideal - lebih baik untuk menentukan dengan lebih tepat.
HorseloverFat
3

Berikut adalah tempat-tempat yang saya gunakan kecuali tanpa tipe

  1. prototipe cepat dan kotor

Itulah penggunaan utama dalam kode saya untuk pengecualian yang tidak dicentang

  1. fungsi main () tingkat atas, di mana saya mencatat setiap pengecualian yang tidak tertangkap

Saya selalu menambahkan ini, sehingga kode produksi tidak menumpahkan stacktraces

  1. di antara lapisan aplikasi

Saya punya dua cara untuk melakukannya:

  • Cara pertama untuk melakukannya: ketika lapisan tingkat yang lebih tinggi memanggil fungsi tingkat yang lebih rendah, itu membungkus panggilan dalam pengecualian yang diketik untuk menangani pengecualian tingkat bawah "atas". Tetapi saya menambahkan pernyataan pengecualian umum, untuk mendeteksi pengecualian tingkat bawah yang tidak tertangani di fungsi tingkat yang lebih rendah.

Saya lebih suka dengan cara ini, saya merasa lebih mudah untuk mendeteksi pengecualian mana yang seharusnya ditangkap dengan tepat: saya "melihat" masalah dengan lebih baik ketika pengecualian tingkat yang lebih rendah dicatat oleh tingkat yang lebih tinggi

  • Cara kedua untuk melakukannya: setiap fungsi tingkat atas dari lapisan tingkat yang lebih rendah memiliki kodenya yang dibungkus secara umum kecuali, untuk itu menangkap semua pengecualian yang tidak tertangani pada lapisan tertentu itu.

Beberapa rekan kerja lebih suka cara ini, karena cara ini mempertahankan pengecualian tingkat yang lebih rendah di fungsi tingkat yang lebih rendah, di mana mereka "termasuk".

nipil
sumber
-14

Coba ini:

try:
    #code
except ValueError:
    pass

Saya mendapat jawaban dari tautan ini, jika ada orang lain yang mengalami masalah ini Periksa

Mekanik
sumber