yang memiliki efek samping yang tidak menyenangkan dari penangkapan KeyboardInterrupt
dan SystemExit
. Saya sebelumnya telah mengambil pandangan bahwa pada dasarnya tidak apa-apa jika except
blok berisi raise
kosong
try:
...
except:
...
raise
… tetapi dalam praktiknya para pemula selalu membuat kesalahan dengan ini. Anda selalu dapat menulis dengan sangat nyaman
except Exception:
_atau bahkan
except BaseException:
yang terakhir menawarkan kemungkinan yang sangat kecil bahwa pemula akan menggunakannya secara tidak sengaja. Jadi saya sampai pada gagasan bahwa "eksplisit lebih baik daripada implisit"
Bayangkan Anda sedang membangun aplikasi blog. Dalam alur normal aplikasi blog Anda, pengguna dapat mendaftar dan membuat postingan blog
Pengecualian [kesalahan] dikatakan terjadi saat aliran normal aplikasi Anda tiba-tiba terganggu. Misalnya, saat pengguna mendaftar dengan email yang ada di situs blog Anda
Penanganan pengecualian berarti memutuskan apa yang harus dilakukan dengan gangguan ini sebagai pengembang aplikasi. Misalnya, dalam skenario ini, kami cukup mengembalikan pesan kesalahan ke pengguna kami, menulis kesalahan ke sistem logging kami, dll
Penanganan kesalahan adalah bagian penting dalam membangun aplikasi apa pun, karena ini membuat kode kita lebih mudah dipelihara
Untuk memunculkan pengecualian di Python, kita cukup melakukan hal berikut
raise ValueError["Something has gone wrong."]
Pada artikel ini, saya bermaksud untuk membagikan beberapa tip penanganan kesalahan Python dan beberapa praktik buruk umum yang harus dihindari
TL;DR
- Jangan pernah menggunakan telanjang
except
- Hindari menangkap atau menaikkan generik
_0# Do NOT ever use bare exception: try: return fetch_all_books[] except: # !!! raise
- Menahan diri dari hanya melewati
except
blok
1. Hindari Telanjang except
Aturan praktis pertama adalah benar-benar menghindari penggunaan except
telanjang, karena tidak memberi kita pengecualian objek untuk diperiksa
Selain itu, menggunakan except
juga menangkap semua pengecualian, termasuk pengecualian yang umumnya tidak kita inginkan, seperti
# Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
5 atau # Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
6Berikut adalah contoh penggunaan kecuali telanjang, yang tidak disarankan
# Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
Jangan pernah menggunakan telanjang kecualiJadi, apa salahnya menangkap setiap pengecualian?
Menangkap setiap pengecualian dapat menyebabkan aplikasi kita gagal tanpa kita benar-benar tahu alasannya. Ini adalah ide yang buruk dalam hal debugging
Ini sebuah contoh. kami sedang membangun fitur yang memungkinkan pengguna mengunggah file PDF. Bayangkan kita menempatkan try-catch umum di sekitar blok kode itu
Di bawah ini, kami menangkap pengecualian umum yang mengatakan file tidak ditemukan apa pun masalah sebenarnya
def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
_Contoh mengapa kita tidak boleh menaikkan atau menangkap generik # Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
0Terlepas dari masalah sebenarnya [mis. g. titik akhir bersifat hanya-baca, pengguna tidak memiliki izin, atau jenis file tidak valid], siapa pun yang kesulitan mengunggah file hanya akan mendapatkan kembali kesalahan umum yang mengklaim bahwa file tidak ditemukan
Sekarang, bayangkan file diunggah, tetapi pengguna malah mengunggah file gambar. Pengguna terus mendapatkan kembali kesalahan "file tidak ditemukan" saat mereka mencoba lagi dan lagi. Akhirnya, mereka mulai berteriak, “Filenya ada di sana. Aplikasi ini adalah omong kosong. ”
Alternatifnya, cara yang tepat untuk menangani ini adalah dengan memperkenalkan kelas pengecualian khusus untuk menangani ketiga skenario yang disebutkan
2. Berhenti Menggunakan # Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
_8
# Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
Kedua, kita harus menghindari menaikkan
# Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
0 generik dengan Python karena cenderung menyembunyikan bugInilah contoh lain yang harus Anda hindari untuk digunakan
# Do NOT raise a generic Exception:
def get_book_List[]:
try:
if not fetch_books[]:
raise Exception["This exception will not be caught by specific catch"] # !!!
except ValueError as e:
print["This doesn't catch Exception"]
raise
get_book_List[]
# Exception: general exceptions not caught by specific handling
Hindari anti-pola penyembunyi kesalahan iniMeskipun ada banyak cara untuk menulis kode yang buruk, ini adalah salah satu anti-pola terburuk [dikenal sebagai penyembunyian kesalahan]
Dalam pengalaman pribadi saya, pola ini menonjol sebagai penguras terbesar pada produktivitas pengembang
3. Berhenti Menggunakan def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
_0
def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
Sebagai pengembang, kami cenderung membungkus kode fungsi kami dengan blok coba-kecuali pada mode autopilot. Kami senang melakukan ini karena kami tahu bahwa selalu ada kemungkinan terjadinya pengecualian
Tidak pernah bisa terlalu aman, kan?
# Do NOT catch with a generic Exception:
def fetch_all_books[]:
try:
if not fetch_all_books[]:
raise DoesNotExistError["No books found!"]
except Exception as e: # !!!
print[e]
Berhenti menggunakan except
# Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
0Namun, peringatannya adalah bahwa pengembang cenderung menangkap pengecualian dengan kelas
def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
3 atau # Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
0 generikDalam skenario ini, itu berarti kami akan menangkap semuanya. Termasuk pengecualian yang tidak dapat atau seharusnya tidak dapat kami pulihkan
Rencanakan, rencanakan, dan rencanakan
Sebaliknya, kita harus selalu merencanakan dan mencari tahu apa yang dapat dilanggar dan pengecualian apa yang diharapkan terjadi
Misalnya, jika kita bekerja dengan panggilan basis data untuk mengambil profil pengguna dengan email, kita harus memperkirakan bahwa email tersebut mungkin tidak ada dan menanganinya sesuai dengan itu.
Dalam skenario ini, kami dapat memunculkan kesalahan
def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
5 khusus dan meminta pengguna kami untuk mencoba lagi, memungkinkan aplikasi kami pulih secara positif dari pengecualianBerikut adalah contoh yang sangat mendasar tentang cara memunculkan pengecualian yang ditentukan pengguna khusus dengan Python
class UserDoesNotExist[Exception]:
"""Raised when user does not exist"""
pass
Sebelum menulis pengecualian yang ditentukan pengguna khusus kami, kami harus selalu memeriksa apakah perpustakaan yang kami gunakan memiliki pengecualian bawaan yang memenuhi kasus penggunaan kami
Singkatnya, kita hanya boleh menangkap kesalahan yang kita minati dengan
def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
6 tertentu yang secara semantik cocok dengan kasus penggunaan kitaTerakhir, inilah contoh yang lebih baik tentang cara menangani pengecualian dengan benar
# Do:
def fetch_user_profile[id]:
try:
user = get_user_profile[id]
if not user:
raise UserDoesNotExist["User ID does not exist."] # Raise specific exception
except UserDoesNotExist as e: # Catch specific exception
logger.exception[e] # Logs the exception
raise # Just raise
# raise UserDoesNotExist # Don't do this or you'll lose the stack trace
Kita dapat menambahkan pengecualian yang lebih spesifik untuk menangani pengecualian yang berbedaTapi saya tidak tahu pengecualian apa yang harus digunakan
Maklum, sangat tidak mungkin kita selalu siap untuk setiap kemungkinan pengecualian
Dalam kasus seperti itu, beberapa orang menyarankan agar kita setidaknya menangkap mereka dengan an, karena itu tidak termasuk hal-hal seperti
def upload_proof_of_address_doc[file]:
try:
result = upload_pdf_to_s3[file]
if not result:
raise Exception["File not found!"]
except:
raise
7, # Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
5, dan # Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
6, yang akan menghentikan aplikasi kitaSaya berpendapat bahwa kita harus meluangkan waktu untuk mencari tahu apa pengecualiannya. Saat menangkap pengecualian umum menjadi kebiasaan, itu menjadi lereng yang licin
Inilah contoh bagus lainnya mengapa kita tidak boleh menangkap pengecualian umum
4. Menahan Diri Dari Melewati except
Blok
Saat mendesain aplikasi, mungkin ada pengecualian khusus di mana kita baik-baik saja tanpa melakukan apa pun
Namun, kemungkinan terburuk yang dapat dilakukan pengembang adalah sebagai berikut
# Do NOT ever pass a bare exception:
try:
compute_combination_sum[value]
except:
pass
# Do NOT do this:
try:
compute_combination_sum[value]
except BaseException:
pass
Ini contoh mengerikan yang tidak boleh kita gunakanKode di atas menyiratkan bahwa meskipun kami tidak siap untuk pengecualian apa pun, kami menangkap pengecualian apa pun dengan sukarela
Kerugian lain dari meneruskan dan menangkap
# Do NOT ever use bare exception:
try:
return fetch_all_books[]
except: # !!!
raise
_0 [atau telanjang except
] adalah bahwa kita tidak akan pernah tahu kesalahan kedua ketika ada dua kesalahan dalam kode. Kesalahan pertama akan selalu ditangkap terlebih dahulu dan kita akan keluar dari # Do NOT raise a generic Exception:
def get_book_List[]:
try:
if not fetch_books[]:
raise Exception["This exception will not be caught by specific catch"] # !!!
except ValueError as e:
print["This doesn't catch Exception"]
raise
get_book_List[]
# Exception: general exceptions not caught by specific handling
3 blokJika kita hanya menyampaikan pernyataan except
_, itu pertanda baik bahwa kita tidak benar-benar siap untuk pengecualian yang kita tangkap. Mungkin ini saat yang tepat untuk memikirkan kembali dan memfaktor ulang
Masuk, jangan lewat
Namun demikian, jika kita tidak perlu melakukan apa-apa tentang pengecualian, setidaknya kita harus menggunakan pengecualian yang lebih spesifik sambil mencatat pengecualian tersebut
import logging
logger = logging.getLogger[__name__]
try:
parse_number[value]
except ValueError as e:
logger.exception[e]
Log, jangan lewatSelain mempertimbangkan untuk menyertakan beberapa kode pemulihan, kami juga dapat menambahkan komentar untuk memberi tahu pengembang lain tentang kasus penggunaan
Intinya adalah, kita harus menghindari melewati kecuali blok kecuali diinginkan secara eksplisit. Sekali lagi, ini biasanya pertanda buruk
Pada akhirnya, kita harus mencatat pengecualian ke sistem pemantauan sehingga kita setidaknya memiliki catatan tentang apa yang sebenarnya salah
Menutup Pikiran
Untuk meringkas semua yang kita lalui dalam artikel ini, kita harus
- Jangan pernah menggunakan telanjang
except
- Berhenti meningkatkan
0 generik# Do NOT ever use bare exception: try: return fetch_all_books[] except: # !!! raise
- Berhenti menangkap generik
_0# Do NOT ever use bare exception: try: return fetch_all_books[] except: # !!! raise
- Hindari hanya melewati
except
blok
Dalam sebagian besar situasi, sering kali lebih baik aplikasi gagal pada titik pengecualian daripada membiarkan aplikasi kita terus berperilaku dengan cara aneh yang tidak terduga. Oleh karena itu, yang terbaik adalah hanya menangkap pengecualian yang kami ketahui dan ingin kami tangani