2.26. Melempar error¶
Sebuah fungsi dapat memberi sinyal masalah kepada pemanggilnya dengan melempar exception. Kata kuncinya adalah raise:
def square_root(x):
if x < 0:
raise ValueError("square_root expects a non-negative number")
return x ** 0.5
Memanggil square_root(-1) berhenti pada baris raise, keluar dari fungsi, dan mencari except yang cocok di pemanggil. Jika tidak ada pemanggil yang menangkapnya, skrip berakhir dengan traceback.
2.26.1. Mengapa melempar daripada mengembalikan sentinel¶
Dua cara untuk melaporkan "input buruk":
# signal with a sentinel
def square_root_or_none(x):
if x < 0:
return None
return x ** 0.5
# raise an exception
def square_root(x):
if x < 0:
raise ValueError("...")
return x ** 0.5
Bentuk exception biasanya lebih baik:
Pemanggil harus sengaja menangani kasus error -- baik dengan
try, atau dengan membiarkan exception disebarkan. Sentinel mudah dilupakan dan mudah disalahartikan sebagai hasil normal.Pesan error berjalan bersama exception; pendekatan sentinel harus melampirkan diagnostik di tempat lain.
Perilaku default pada exception yang tidak ditangani adalah crash keras dengan traceback yang menunjuk ke panggilan yang bermasalah. Pengembalian
Nonediam-diam menjadi bug halus di kemudian hari.
Gunakan sentinel hanya ketika "tidak ditemukan" adalah hasil yang rutin dan tidak luar biasa -- dict.get() mengembalikan None pada kunci yang hilang justru karena pencarian diharapkan terkadang tidak berhasil.
2.26.2. Kelas exception kustom¶
Untuk melempar masalah yang mungkin ingin dibedakan pemanggil dari error bawaan, definisikan subkelas dari Exception:
class ConfigError(Exception):
pass
def load_config(path):
try:
f = open(path)
except OSError as e:
raise ConfigError("missing config file: " + path)
try:
load_config("settings.json")
except ConfigError as e:
print("startup failed:", e)
Badan class yang kosong tidak masalah -- namanya sendiri yang penting, karena pemanggil menangkap berdasarkan kelas. Kelompokkan error terkait di bawah basis umum jika pemanggil mungkin ingin menangkap seluruh keluarga dalam satu blok.
2.26.2.1. Re-raising¶
Bare raise di dalam blok except melempar kembali exception saat ini sehingga disebarkan ke handler berikutnya:
try:
do_work()
except Exception as e:
log(e)
raise # let it keep going
Ini adalah bentuk yang tepat ketika sebuah fungsi ingin mengamati error (mencatatnya, menghitungnya, membatalkan perubahan parsial) tanpa benar-benar menanganinya.
2.26.3. Kapan menangkap dan kapan menyebarkan¶
Panduan praktis yang berguna:
Tangkap exception pada level yang dapat bermakna pulih -- substitusi default, coba lagi, lewati input yang buruk.
Biarkan disebarkan ketika tidak ada yang berguna untuk dilakukan kecuali crash, atau ketika lapisan di atas adalah yang tahu cara memulihkan.
Sebuah fungsi di tengah call stack yang menelan setiap error dan kembali secara diam-diam membuat kegagalan tidak dapat dilacak. Lebih baik biarkan exception berjalan hingga mencapai kode yang benar-benar memiliki rencana.