Menghindari Jebakan Umum: Penanganan Eksepsi yang Benar dalam Python
Saya melihat banyak orang menangani eksepsi dengan cara yang salah. Mungkin ini berlaku untuk Anda juga. Apakah situasi berikut terdengar familiar?
Anda sedang menulis beberapa kode, dan Anda tahu bahwa perpustakaan yang Anda gunakan mungkin memunculkan eksepsi. Anda tidak ingat eksepsi mana, tepatnya. Pada titik ini, menggoda untuk menggunakan blok catch-all dan melanjutkan dengan hal-hal menyenangkan.
Daftar isi
Cara terburuk untuk melakukannya
Yang terburuk yang bisa Anda lakukan adalah membuat blok try-except yang menangkap apa pun. Dengan catch-all, saya maksud sesuatu seperti:
try: ... except: pass
Blok catch-all semacam ini buruk karena:
Anda tidak tahu eksepsi lain apa yang mungkin muncul (selengkapnya nanti).
Kami menyembunyikan eksepsi dengan menggunakan pass secara diam-diam alih-alih mencatat kesalahan.
Selain itu, except kosong akan menangkap semuanya, termasuk KeyboardInterrupt (control + c), SystemExit, dan bahkan NameErrors! Ini berarti kode berikut tidak dapat dihentikan secara bersih:
from time import sleep while True: try: print("Try and stop me") sleep(1) except: print("Don't.. stop.. me now!")
Silakan coba. Anda perlu menutup jendela terminal atau membunuh proses Python untuk menghentikan program ini.
Cara yang cukup baik untuk menangkap semua eksepsi
Di sisi lain, saat menggunakan except Exception, meskipun masih merupakan cara cepat dan kotor untuk menangkap terlalu banyak eksepsi, setidaknya Anda akan dapat menghentikan proses yang berjalan dengan benar:
from time import sleep while True: try: print("Try and stop me") sleep(1) except Exception: print("Ok I'll stop!")
Saat menangkap Exception Anda tidak akan menangkap SystemExit, KeyboardInterrupt dan eksepsi semacam itu. Mengapa demikian, Anda tanya?
Semua eksepsi berasal dari kelas yang disebut BaseException. Menurut dokumentasi resmi: “Dalam pernyataan try dengan klausa except yang menyebut kelas tertentu, klausa tersebut juga menangani kelas eksepsi yang berasal dari kelas tersebut.” Except kosong setara dengan except BaseException, oleh karena itu akan menangkap semua eksepsi yang mungkin.
Di sisi lain, kelas Exception didefinisikan sebagai: “Semua eksepsi bawaan, non-sistem-keluar berasal dari kelas ini. Semua eksepsi yang didefinisikan pengguna juga harus berasal dari kelas ini.”
Itu menjadi lebih buruk
Dalam contoh berikut, kami menggunakan perpustakaan os untuk mendapatkan direktori kerja saat ini. Namun, jari-jari gemuk saya membuat kesalahan ketik:
import os try: working_dir = os.getcdw() print(working_dir) except: print('error')
Karena os.getcdw bukan fungsi dalam modul os, NameError dilemparkan. Alih-alih gagal, klausa except akan menangkap kesalahan, mencetak ‘error’ dan program akan terus berjalan meskipun kesalahan ketik yang jelas kita lakukan. Sayangnya, ini tidak dapat diselesaikan dengan menangkap Exception juga!
Sepertinya, trik kecil kami dari langkah satu bukan solusi untuk semua masalah kami. Jadi apa yang harus kita lakukan?
Tangkap apa yang dapat Anda tangani
Sebuah frasa yang sering didengar tentang eksepsi adalah: tangkap apa yang dapat Anda tangani. Banyak pengembang tertarik untuk langsung menangani eksepsi, sementara seringkali lebih baik untuk membiarkan eksepsi menyebar ke bagian program Anda yang sebenarnya dapat menanganinya.
Misalnya, pertimbangkan bagian editor teks yang membuka dan memuat file, mari kita sebut kelas OpenFile. Jika pengguna meminta membuka file yang tidak ada, Anda dapat langsung menangani kesalahan itu, atau membiarkannya menyebar.
Dalam kasus ini, lebih baik untuk menyebarkan eksepsi ke pemanggil, karena OpenFile tidak tahu seberapa buruk eksepsi ini bagi pemanggil. Pemanggil dapat menangani situasi dengan beberapa cara:
Itu dapat membuat file baru dengan nama itu dan melanjutkan
Mungkin pemanggil perlu file untuk ada, dalam hal ini ia dapat menampilkan dialog kesalahan untuk memberitahu pengguna bahwa file ini tidak ada.
Apapun caranya, bukan tanggung jawab kelas OpenFile untuk memutuskan apa yang harus dilakukan dalam kasus FileNotFoundError.
Jadi apakah eksepsi harus selalu menyebar? Tidak. Eksepsi yang mungkin dapat ditangani dalam kelas FileOpen adalah TimeoutError. Anda mungkin ingin mencoba beberapa kali, misalnya, tanpa mengganggu pemanggil dengan kesalahan. Ini adalah eksepsi yang dapat ditangani oleh OpenFile, jadi tidak apa-apa untuk menangkapnya dan mencoba lagi.
Kesimpulan
Anda tidak boleh dalam keadaan apa pun menangkap lebih banyak eksepsi daripada yang dapat Anda tangani. Blok except umum adalah resep untuk bug dan kode yang tidak dapat diprediksi. Dengan kata lain: tangkap apa yang dapat Anda tangani.
Jika Anda menulis kode Anda dengan matra ‘tangkap apa yang dapat Anda tangani’ dalam pikiran, menulis blok catch-all adalah melanggar semua aturan. Jadi tolong, hentikan itu. Sebagai latihan, Anda dapat memeriksa beberapa kode yang ada Anda dan melihat apakah dapat diperbaiki dengan pengetahuan baru ini!