Konsensus umum “jangan gunakan pengecualian!” Sebagian besar berasal dari bahasa lain dan bahkan ada yang sudah usang.
Dalam C ++, melempar pengecualian sangat mahal karena “stack unwinding”. Setiap deklarasi variabel lokal seperti with
pernyataan dalam Python, dan objek dalam variabel itu dapat menjalankan destruktor. Destructor ini dieksekusi ketika pengecualian dilemparkan, tetapi juga ketika kembali dari suatu fungsi. “RAII idiom” ini adalah fitur bahasa yang tidak terpisahkan dan sangat penting untuk menulis kode yang kuat dan benar - jadi RAII versus pengecualian murah merupakan tradeoff yang C ++ putuskan untuk RAII.
Pada awal C ++, banyak kode tidak ditulis dengan cara pengecualian-aman: kecuali Anda benar-benar menggunakan RAII, mudah bocor memori dan sumber daya lainnya. Jadi, melempar pengecualian akan membuat kode itu salah. Ini tidak lagi masuk akal karena bahkan pustaka standar C ++ menggunakan pengecualian: Anda tidak bisa berpura-pura pengecualian tidak ada. Namun, pengecualian masih menjadi masalah saat menggabungkan kode C dengan C ++.
Di Jawa, setiap pengecualian memiliki jejak tumpukan yang terkait. Jejak tumpukan sangat berharga saat men-debug kesalahan, tetapi usaha yang sia-sia ketika pengecualian tidak pernah dicetak, misalnya karena itu hanya digunakan untuk aliran kontrol.
Jadi dalam bahasa-bahasa itu pengecualian "terlalu mahal" untuk digunakan sebagai aliran kontrol. Dalam Python ini kurang dari masalah dan pengecualian jauh lebih murah. Selain itu, bahasa Python sudah menderita beberapa overhead yang membuat biaya pengecualian tidak terlihat dibandingkan dengan konstruksi aliran kontrol lainnya: misalnya memeriksa apakah entri dikt ada dengan tes keanggotaan eksplisitif key in the_dict: ...
umumnya sama cepatnya dengan hanya mengakses entri the_dict[key]; ...
dan memeriksa apakah Anda dapatkan KeyError. Beberapa fitur bahasa integral (misalnya generator) dirancang dalam hal pengecualian.
Jadi, sementara tidak ada alasan teknis untuk secara khusus menghindari pengecualian dalam Python, masih ada pertanyaan apakah Anda harus menggunakannya alih-alih mengembalikan nilai. Masalah tingkat desain dengan pengecualian adalah:
sama sekali tidak jelas. Anda tidak dapat dengan mudah melihat fungsi dan melihat pengecualian yang dilemparkannya, sehingga Anda tidak selalu tahu harus menangkap apa. Nilai kembali cenderung lebih didefinisikan dengan baik.
pengecualian adalah aliran kontrol non-lokal yang memperumit kode Anda. Saat Anda melempar pengecualian, Anda tidak tahu di mana aliran kontrol akan dilanjutkan. Untuk kesalahan yang tidak dapat segera ditangani, ini mungkin ide yang baik, ketika memberi tahu penelepon Anda tentang kondisi ini sama sekali tidak perlu.
Budaya Python umumnya miring mendukung pengecualian, tetapi mudah untuk berlebihan. Bayangkan sebuah list_contains(the_list, item)
fungsi yang memeriksa apakah daftar tersebut berisi item yang sama dengan item itu. Jika hasilnya dikomunikasikan melalui pengecualian yang benar-benar menjengkelkan, karena kita harus menyebutnya seperti ini:
try:
list_contains(invited_guests, person_at_door)
except Found:
print("Oh, hello {}!".format(person_at_door))
except NotFound:
print("Who are you?")
Mengembalikan bool akan jauh lebih jelas:
if list_contains(invited_guests, person_at_door):
print("Oh, hello {}!".format(person_at_door))
else:
print("Who are you?")
Jika fungsi sudah seharusnya mengembalikan nilai, maka mengembalikan nilai khusus untuk kondisi khusus agak rawan kesalahan, karena orang akan lupa untuk memeriksa nilai ini (itu mungkin penyebab 1/3 dari masalah di C). Pengecualian biasanya lebih tepat.
Contoh yang baik adalah pos = find_string(haystack, needle)
fungsi yang mencari kemunculan pertama needle
string dalam string `haystack, dan mengembalikan posisi awal. Tetapi bagaimana jika tali jerami tidak mengandung benang jarum?
Solusi oleh C dan ditiru oleh Python adalah mengembalikan nilai khusus. Dalam C ini adalah pointer nol, dengan Python ini -1
. Ini akan menghasilkan hasil yang mengejutkan ketika posisi digunakan sebagai indeks string tanpa memeriksa, terutama seperti -1
indeks yang valid dalam Python. Di C, pointer NULL Anda setidaknya akan memberi Anda segfault.
Dalam PHP, nilai khusus dari jenis yang berbeda dikembalikan: boolean FALSE
bukan bilangan bulat. Ternyata ini sebenarnya tidak lebih baik karena aturan konversi implisit bahasa (tetapi perhatikan bahwa dalam Python juga boolean dapat digunakan sebagai ints!). Fungsi yang tidak mengembalikan tipe yang konsisten umumnya dianggap sangat membingungkan.
Varian yang lebih kuat adalah dengan melemparkan pengecualian ketika string tidak dapat ditemukan, yang memastikan bahwa selama aliran kontrol normal tidak mungkin untuk secara tidak sengaja menggunakan nilai khusus sebagai pengganti nilai biasa:
try:
pos = find_string(haystack, needle)
do_something_with(pos)
except NotFound:
...
Atau, selalu mengembalikan jenis yang tidak dapat digunakan secara langsung tetapi harus dibuka dulu dapat digunakan, misalnya tuple bool hasil di mana boolean menunjukkan apakah pengecualian terjadi atau jika hasilnya dapat digunakan. Kemudian:
pos, ok = find_string(haystack, needle)
if not ok:
...
do_something_with(pos)
Ini memaksa Anda untuk menangani masalah dengan segera, tetapi itu menjengkelkan dengan sangat cepat. Ini juga mencegah Anda dari fungsi chaining dengan mudah. Setiap panggilan fungsi sekarang membutuhkan tiga baris kode. Golang adalah bahasa yang menganggap gangguan ini layak untuk keselamatan.
Jadi untuk meringkas, pengecualian tidak sepenuhnya tanpa masalah dan dapat digunakan secara berlebihan, terutama ketika mereka mengganti nilai pengembalian "normal". Tetapi ketika digunakan untuk memberi sinyal kondisi khusus (tidak harus hanya kesalahan), maka pengecualian dapat membantu Anda mengembangkan API yang bersih, intuitif, mudah digunakan, dan sulit disalahgunakan.
collections.defaultdict
ataumy_dict.get(key, default)
membuat kode lebih jelas daripadatry: my_dict[key] except: return default
TIDAK! - tidak secara umum - pengecualian tidak dianggap sebagai praktik kontrol aliran yang baik dengan pengecualian kelas kode tunggal. Satu tempat di mana pengecualian dianggap sebagai cara yang masuk akal, atau bahkan lebih baik, untuk memberi sinyal suatu kondisi adalah operasi generator atau iterator. Operasi ini dapat mengembalikan nilai yang mungkin sebagai hasil yang valid sehingga diperlukan mekanisme untuk memberi sinyal penyelesaian.
Pertimbangkan untuk membaca file biner stream satu byte pada satu waktu - benar-benar nilai apa pun adalah hasil yang berpotensi valid tetapi kita masih perlu memberi sinyal akhir file. Jadi kita punya pilihan, kembalikan dua nilai, (nilai byte & tanda yang valid), setiap kali atau ajukan pengecualian ketika tidak ada lagi yang harus dilakukan. Dalam dua kasus kode konsumsi dapat terlihat seperti:
kalau tidak:
Tapi ini sudah, sejak PEP 343 diimplementasikan & porting kembali, semua rapi terbungkus dalam
with
pernyataan. Di atas menjadi, sangat pythonic:Dalam python3 ini menjadi:
Saya sangat mendorong Anda untuk membaca PEP 343 yang memberikan latar belakang, alasan, contoh, dll.
Juga biasa menggunakan pengecualian untuk menandai akhir pemrosesan saat menggunakan fungsi generator untuk memberi sinyal akhir.
Saya ingin menambahkan bahwa contoh pencari Anda hampir pasti mundur, fungsi tersebut harus menjadi generator, mengembalikan kecocokan pertama pada panggilan pertama, kemudian panggilan substituen mengembalikan kecocokan berikutnya, dan memunculkan
NotFound
pengecualian ketika tidak ada lagi kecocokan.sumber
if
akan lebih baik. Ketika datang untuk debugging & pengujian atau bahkan alasan tentang perilaku kode Anda, lebih baik tidak mengandalkan pengecualian - mereka harus menjadi pengecualian. Saya telah melihat, dalam kode produksi, perhitungan yang dikembalikan melalui lemparan / tangkapan alih-alih pengembalian sederhana ini berarti bahwa ketika perhitungan mencapai pembagian dengan kesalahan nol, ia mengembalikan nilai acak daripada menabrak di mana kesalahan terjadi hal ini menyebabkan beberapa jam debugging.