Mengapa "kecuali: lulus" praktik pemrograman yang buruk?

325

Saya sering melihat komentar pada pertanyaan Stack Overflow lainnya tentang bagaimana penggunaan except: passtidak disarankan. Kenapa ini buruk? Kadang-kadang saya hanya tidak peduli apa kesalahannya, dan saya ingin melanjutkan dengan kodenya.

try:
    something
except:
    pass

Mengapa menggunakan except: passblok buruk? Apa yang membuatnya buruk? Apakah ini fakta bahwa saya passmelakukan kesalahan atau saya exceptsalah?

Vader
sumber
1
Paling tidak, saya sarankan Anda mencatatnya, jadi Anda TAHU masalah apa yang Anda abaikan. Gunakan loggingmodul pada tingkat DEBUG untuk menghindari mereka mengalir keluar dalam produksi, tetapi tetap tersedia dalam pengembangan.
pcurry

Jawaban:

346

Seperti benar Anda menebak, ada dua sisi untuk itu: Penangkapan setiap kesalahan dengan menetapkan tidak ada jenis terkecuali setelah except, dan hanya lewat itu tanpa mengambil tindakan apapun.

Penjelasan saya adalah "sedikit" lebih lama — jadi t; dr itu dipecah menjadi ini:

  1. Jangan sampai ada kesalahan . Selalu tentukan pengecualian mana yang Anda siap untuk pulihkan dan hanya menangkapnya.
  2. Cobalah untuk menghindari lewat kecuali blok . Kecuali diinginkan secara eksplisit, ini biasanya bukan pertanda baik.

Tapi mari kita masuk ke detail:

Jangan sampai ada kesalahan

Saat menggunakan tryblok, Anda biasanya melakukan ini karena Anda tahu bahwa ada kemungkinan pengecualian dilemparkan. Dengan demikian, Anda juga sudah memiliki perkiraan tentang apa yang bisa dipatahkan dan pengecualian apa yang bisa dilemparkan. Dalam kasus seperti itu, Anda menangkap pengecualian karena Anda dapat memulihkannya secara positif . Itu berarti bahwa Anda siap untuk pengecualian dan memiliki beberapa rencana alternatif yang akan Anda ikuti jika pengecualian itu.

Misalnya, ketika Anda meminta pengguna untuk memasukkan nomor, Anda dapat mengonversi input yang menggunakan int()yang dapat meningkatkan a ValueError. Anda dapat dengan mudah memulihkannya dengan hanya meminta pengguna untuk mencobanya lagi, jadi menangkap ValueErrordan mendorong pengguna lagi akan menjadi rencana yang tepat. Contoh lain adalah jika Anda ingin membaca beberapa konfigurasi dari file, dan file itu kebetulan tidak ada. Karena ini adalah file konfigurasi, Anda mungkin memiliki beberapa konfigurasi default sebagai cadangan, sehingga file tersebut tidak benar-benar diperlukan. Jadi menangkap FileNotFoundErrordan hanya menerapkan konfigurasi default akan menjadi rencana yang bagus di sini. Sekarang dalam kedua kasus ini, kami memiliki pengecualian yang sangat spesifik yang kami harapkan dan memiliki rencana yang sama spesifik untuk pulih darinya. Dengan demikian, dalam setiap kasus, kami hanya secara eksplisit except tertentu pengecualian.

Namun, jika kita ingin menangkap semuanya , maka — selain pengecualian-pengecualian itu kita siap untuk pulih dari — ada juga kemungkinan bahwa kita mendapatkan pengecualian yang tidak kita harapkan, dan yang memang tidak bisa kita pulihkan dari; atau tidak seharusnya pulih dari.

Mari kita ambil contoh file konfigurasi dari atas. Seandainya ada file yang hilang, kami hanya menerapkan konfigurasi default kami, dan mungkin memutuskan di lain waktu untuk secara otomatis menyimpan konfigurasi (jadi lain kali, file itu ada). Sekarang bayangkan kita mendapat IsADirectoryError, atau aPermissionErrorsebagai gantinya. Dalam kasus seperti itu, kami mungkin tidak ingin melanjutkan; kami masih bisa menerapkan konfigurasi default kami, tetapi kami nanti tidak akan dapat menyimpan file. Dan sepertinya pengguna juga memiliki konfigurasi khusus, jadi menggunakan nilai default kemungkinan tidak diinginkan. Jadi kami ingin memberi tahu pengguna tentang hal itu segera, dan mungkin membatalkan eksekusi program juga. Tapi itu bukan sesuatu yang ingin kita lakukan di suatu tempat jauh di dalam beberapa bagian kode kecil; ini adalah sesuatu yang penting di level aplikasi, jadi itu harus ditangani di atas — jadi biarkan pengecualian muncul.

Contoh sederhana lain juga disebutkan dalam dokumen idiom Python 2 . Di sini, salah ketik sederhana ada dalam kode yang menyebabkannya rusak. Karena kami menangkap setiap pengecualian, kami juga menangkap NameErrors dan SyntaxErrors . Keduanya adalah kesalahan yang terjadi pada kita semua saat pemrograman; dan keduanya adalah kesalahan yang sama sekali tidak ingin kami sertakan saat mengirimkan kode. Tetapi karena kami juga menangkapnya, kami bahkan tidak tahu bahwa itu terjadi di sana dan kehilangan bantuan untuk men-debug dengan benar.

Tetapi ada juga pengecualian yang lebih berbahaya yang tidak mungkin kita siapkan. Misalnya SystemError biasanya sesuatu yang jarang terjadi dan yang tidak bisa kita rencanakan; itu berarti ada sesuatu yang lebih rumit sedang terjadi, sesuatu yang kemungkinan mencegah kita melanjutkan tugas saat ini.

Bagaimanapun, sangat tidak mungkin bahwa Anda siap untuk semuanya dalam bagian skala kecil dari kode, jadi di situlah Anda hanya perlu menangkap pengecualian yang telah Anda siapkan. Beberapa orang menyarankan untuk menangkap setidaknya Exceptionkarena tidak akan mencakup hal-hal seperti SystemExitdan KeyboardInterruptyang dengan desain untuk menghentikan aplikasi Anda, tetapi saya berpendapat bahwa ini masih terlalu spesifik. Hanya ada satu tempat di mana saya secara pribadi menerima penangkapan Exceptionatau sembarang tempatpengecualian, dan itu ada dalam penangan pengecualian tingkat aplikasi global tunggal yang memiliki tujuan tunggal untuk mencatat pengecualian apa pun yang tidak kami persiapkan. Dengan begitu, kita masih dapat menyimpan sebanyak mungkin informasi tentang pengecualian yang tidak terduga, yang kemudian dapat kita gunakan untuk memperluas kode kita untuk menangani kode-kode itu secara eksplisit (jika kita dapat memulihkannya) atau — jika ada bug — untuk membuat kasus uji untuk memastikan itu tidak akan terjadi lagi. Tapi tentu saja, itu hanya berfungsi jika kita hanya menangkap pengecualian yang sudah kita harapkan, jadi yang tidak kita harapkan akan muncul secara alami.

Cobalah untuk menghindari lewat kecuali blok

Ketika secara eksplisit menangkap sejumlah kecil pengecualian khusus, ada banyak situasi di mana kita akan baik-baik saja dengan tidak melakukan apa-apa. Dalam kasus seperti itu, memiliki except SomeSpecificException: passsaja tidak apa-apa Namun, sebagian besar waktu, ini tidak terjadi karena kita mungkin memerlukan beberapa kode yang berkaitan dengan proses pemulihan (seperti yang disebutkan di atas). Ini bisa berupa sesuatu yang mencoba lagi tindakan, atau untuk menetapkan nilai default sebagai gantinya.

Jika bukan itu masalahnya, misalnya karena kode kami sudah terstruktur untuk diulangi sampai berhasil, maka hanya lewat saja sudah cukup. Mengambil contoh kami dari atas, kami mungkin ingin meminta pengguna untuk memasukkan nomor. Karena kami tahu bahwa pengguna ingin tidak melakukan apa yang kami minta, kami mungkin hanya memasukkannya ke dalam lingkaran, sehingga bisa terlihat seperti ini:

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

Karena kami terus berusaha sampai tidak ada pengecualian yang dilemparkan, kami tidak perlu melakukan sesuatu yang istimewa di blok kecuali, jadi ini baik-baik saja. Tapi tentu saja, orang mungkin berargumen bahwa kami setidaknya ingin menunjukkan kepada pengguna beberapa pesan kesalahan untuk memberi tahu mengapa ia harus mengulangi input.

Namun dalam banyak kasus lain, hanya lewat dalam exceptadalah tanda bahwa kita tidak benar-benar siap untuk pengecualian yang kita tangkap. Kecuali jika pengecualian itu sederhana (suka ValueErroratau tidak TypeError), dan alasan mengapa kita bisa lulus sudah jelas, cobalah untuk tidak hanya lewat. Jika benar-benar tidak ada yang dapat dilakukan (dan Anda benar-benar yakin tentang hal itu), maka pertimbangkan untuk menambahkan komentar mengapa demikian; jika tidak, perluas blok kecuali untuk benar-benar memasukkan beberapa kode pemulihan.

except: pass

Pelaku terburuk adalah kombinasi keduanya. Ini berarti bahwa kita rela menangkap kesalahan apa pun meskipun kita sama sekali tidak siap untuk itu dan kita juga tidak melakukan apa-apa. Anda setidaknya ingin mencatat kesalahan dan juga kemungkinan mengembalikannya untuk tetap menghentikan aplikasi (sepertinya Anda tidak dapat melanjutkan seperti biasa setelah MemoryError). Melewati saja tidak hanya akan membuat aplikasi tetap hidup (tergantung di mana Anda menangkap tentu saja), tetapi juga membuang semua informasi, sehingga tidak mungkin untuk menemukan kesalahan — yang terutama benar jika Anda bukan orang yang menemukannya.


Jadi intinya adalah: Hanya menangkap pengecualian yang benar-benar Anda harapkan dan siap untuk pulih dari; semua yang lain kemungkinan adalah kesalahan yang harus Anda perbaiki, atau sesuatu yang Anda tidak siap untuk melakukannya. Melewati pengecualian spesifik tidak masalah jika Anda benar-benar tidak perlu melakukan sesuatu tentang hal itu. Dalam semua kasus lain, itu hanya tanda anggapan dan malas. Dan Anda pasti ingin memperbaikinya.

menyodok
sumber
1
"Anda setidaknya ingin mencatat kesalahan dan juga kemungkinan mengembalikannya untuk tetap menghentikan aplikasi". Bisakah Anda mendemonstrasikan bagaimana "mengembalikan" pengecualian untuk membiarkannya terus menggelembung bahkan setelah menangkapnya? Ini sepertinya berguna bagi saya untuk menambahkan beberapa pesan kesalahan khusus sambil tetap membiarkan pengecualian memaksa aplikasi untuk berhenti.
Gabriel Staples
1
Ini membantu memperjelas: mereka menggunakan selimut except, tetapi kemudian memanggil raisetanpa argumen untuk terus membiarkan pengecualian muncul, mengakhiri aplikasi. Saya menyukainya: ianbicking.org/blog/2007/09/re-raising-exceptions.html . Sepertinya pengecualian kuat untuk aturan tentang tidak menggunakan selimut except.
Gabriel Staples
1
@GabrielStaples Ya, pengecualian yang tertangkap dapat dipulihkan menggunakan raise. Anda biasanya hanya melakukan ini di beberapa lokasi dalam aplikasi Anda untuk mencatat pengecualian.
colok
Ini bagus, hindari lewat kecuali blok. Saya akan mengatakan bahwa melakukan apa pun yang tampaknya lebih dimengerti, terutama untuk orang lain. Dapatkan set kedua mata python untuk meninjau kode Anda dan melihat apakah mereka mempertanyakan blok tersebut. Keterbacaan adalah kuncinya.
radtek
262

Masalah utama di sini adalah mengabaikan semua kesalahan: Kehabisan memori, CPU menyala, pengguna ingin berhenti, program ingin keluar, Jabberwocky membunuh pengguna.

Ini terlalu banyak. Di kepala Anda, Anda berpikir "Saya ingin mengabaikan kesalahan jaringan ini". Jika terjadi sesuatu yang tidak terduga , maka kode Anda diam-diam melanjutkan dan memecah dengan cara yang benar-benar tidak dapat diprediksi sehingga tidak ada yang dapat melakukan debug.

Itu sebabnya Anda harus membatasi diri untuk mengabaikan hanya beberapa kesalahan tertentu dan membiarkan sisanya berlalu.

Aaron Digulla
sumber
75

Menjalankan kode pseudo Anda secara harfiah bahkan tidak memberikan kesalahan:

try:
    something
except:
    pass

seolah-olah itu adalah potongan kode yang benar-benar valid, bukannya melempar a NameError. Saya harap ini bukan yang Anda inginkan.

YS-L
sumber
51

Mengapa "kecuali: lulus" praktik pemrograman yang buruk?

Kenapa ini buruk?

try:
    something
except:
    pass

Ini menangkap setiap kemungkinan pengecualian, termasuk GeneratorExit, KeyboardInterrupt, dan SystemExit- yang pengecualian Anda mungkin tidak berniat untuk menangkap. Itu sama dengan menangkap BaseException.

try:
    something
except BaseException:
    pass

Dokumentasi versi lama mengatakan :

Karena setiap kesalahan dalam Python menimbulkan pengecualian, menggunakan except:dapat membuat banyak kesalahan pemrograman terlihat seperti masalah runtime, yang menghambat proses debugging.

Hirarki Pengecualian Python

Jika Anda menangkap kelas pengecualian orang tua, Anda juga menangkap semua kelas anak mereka. Jauh lebih elegan jika hanya menangkap pengecualian yang siap Anda tangani.

Inilah hierarki pengecualian Python 3 - apakah Anda benar-benar ingin menangkap mereka semua ?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

Jangan Lakukan ini

Jika Anda menggunakan bentuk penanganan pengecualian ini:

try:
    something
except: # don't just do a bare except!
    pass

Maka Anda tidak akan dapat mengganggu somethingblok Anda dengan Ctrl-C. Program Anda akan mengabaikan setiap Pengecualian yang mungkin di dalam tryblok kode.

Berikut ini contoh lain yang akan memiliki perilaku yang tidak diinginkan yang sama:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

Alih-alih, cobalah hanya menangkap pengecualian spesifik yang Anda tahu sedang Anda cari. Misalnya, jika Anda tahu Anda mungkin mendapatkan kesalahan nilai pada konversi:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

Penjelasan lebih lanjut dengan contoh lain

Anda mungkin melakukannya karena Anda telah mengikis web dan mengatakan, a UnicodeError, tetapi karena Anda telah menggunakan penangkapan Pengecualian terluas, kode Anda, yang mungkin memiliki kelemahan mendasar lainnya, akan berusaha berjalan hingga selesai, menghabiskan bandwidth , waktu pemrosesan, keausan pada peralatan Anda, kehabisan memori, mengumpulkan data sampah, dll.

Jika orang lain meminta Anda untuk menyelesaikan sehingga mereka dapat mengandalkan kode Anda, saya mengerti merasa terdorong untuk hanya menangani semuanya. Tetapi jika Anda ingin gagal dengan ribut saat Anda berkembang, Anda akan memiliki kesempatan untuk memperbaiki masalah yang mungkin hanya muncul sebentar-sebentar, tetapi itu akan menjadi bug jangka panjang yang mahal.

Dengan penanganan kesalahan yang lebih tepat, kode Anda bisa lebih kuat.

Aaron Hall
sumber
31
>>> import this

Zen Python, oleh Tim Peters

Cantik lebih baik dari yang jelek.
Eksplisit lebih baik daripada implisit.
Sederhana lebih baik daripada kompleks.
Kompleks lebih baik daripada rumit.
Flat lebih baik daripada bersarang.
Jarang lebih baik daripada padat.
Jumlah keterbacaan diperhitungkan.
Kasus khusus tidak cukup istimewa untuk melanggar aturan.
Meskipun kepraktisan mengalahkan kemurnian.
Kesalahan tidak boleh terjadi secara diam-diam.
Kecuali secara eksplisit dibungkam.
Dalam menghadapi ambiguitas, tolak godaan untuk menebak.
Harus ada satu - dan lebih disukai hanya satu - cara yang jelas untuk melakukannya.
Meskipun demikian mungkin tidak jelas pada awalnya kecuali Anda orang Belanda.
Sekarang lebih baik daripada tidak sama sekali.
Meskipun tidak pernah sering lebih baik daripadatepat sekarang.
Jika implementasinya sulit dijelaskan, itu ide yang buruk.
Jika implementasinya mudah dijelaskan, mungkin itu ide yang bagus.
Namespaces adalah salah satu ide bagus - mari kita lakukan lebih banyak lagi!

Jadi, inilah pendapat saya. Setiap kali Anda menemukan kesalahan, Anda harus melakukan sesuatu untuk mengatasinya, yaitu menulisnya di logfile atau sesuatu yang lain. Setidaknya, ini memberi tahu Anda bahwa dulu ada kesalahan.

Pendorong
sumber
64
-1 Argumen dari otoritas sebenarnya tidak menjelaskan apa pun. Otoritas bisa salah.
Izkata
23
apa yang ditulis @Izkata, DAN, satu baris di bawahnya, otoritas yang sama menulis: "Kecuali secara eksplisit dibungkam", yang persis seperti itu kecuali: pass tidak.
Ofri Raviv
13
@OfriRaviv Tidak, bukankah kesalahan itu terjadi secara implisit ? Secara eksplisit akan membutuhkan penamaan kesalahan yang seharusnya untuk lulus diam-diam, yaitu, menjadi eksplisit tentang hal itu . Ini bukan apa-apa kecuali: lulus tidak.
Chelonian
24

Anda harus menggunakan setidaknya except Exception:untuk menghindari menangkap pengecualian sistem seperti SystemExitatau KeyboardInterrupt. Berikut tautan ke dokumen.

Secara umum Anda harus mendefinisikan secara eksplisit pengecualian yang ingin Anda tangkap, untuk menghindari penangkapan pengecualian yang tidak diinginkan. Anda harus tahu pengecualian apa yang Anda abaikan .

Tupteq
sumber
13

Pertama, itu melanggar dua prinsip Zen Python :

  • Eksplisit lebih baik daripada implisit
  • Kesalahan tidak boleh terjadi secara diam-diam

Artinya, Anda sengaja membuat kesalahan Anda diam-diam. Selain itu, Anda tidak tahu peristiwa, kesalahan mana yang sebenarnya terjadi, karena except: passakan menangkap pengecualian.

Kedua, jika kami mencoba untuk abstrak dari Zen Python, dan berbicara dengan kewarasan, Anda harus tahu, bahwa menggunakan except:passmembuat Anda tidak memiliki pengetahuan dan kontrol dalam sistem Anda. Aturan praktisnya adalah untuk meningkatkan pengecualian, jika kesalahan terjadi, dan mengambil tindakan yang sesuai. Jika Anda tidak tahu sebelumnya, tindakan apa yang harus dilakukan, setidaknya catat kesalahan di suatu tempat (dan lebih baik kembalikan pengecualian):

try:
    something
except:
    logger.exception('Something happened')

Tapi, biasanya, jika Anda mencoba menangkap pengecualian, Anda mungkin melakukan kesalahan!

Alexander Zhukov
sumber
2
... Kecuali secara eksplisit dibungkam, yang merupakan kasus dengan OP.
Hyperboreus
Saya ingin tahu solusi Anda. Bahkan, ketika benar-benar tidak perlu dilakukan, saya hanya daftar kesalahan kecuali dan membuat komentar dan menulis log. Lalu lewat begitu saja.
Booster
2
@ Hyperboreus, saya tidak berpikir, bahwa menangkap semua dan kesalahan secara eksplisit membungkam mereka, yaitu, Anda bahkan tidak tahu, apa yang Anda tangkap.
Alexander Zhukov
13
"Karena beberapa pria mengatakan demikian" sebenarnya bukan jawaban untuk "Kenapa?" pertanyaan.
Sebastian Negraszus
12

The except:passkonstruk dasarnya membungkam kondisi luar biasa setiap dan semua yang muncul sedangkan kode tercakup dalam try:blok yang sedang dijalankan.

Apa yang membuat praktik buruk ini adalah bahwa itu biasanya bukan yang Anda inginkan. Lebih sering, beberapa kondisi spesifik muncul yang Anda ingin hening, dan except:passterlalu banyak instrumen tumpul. Ini akan menyelesaikan pekerjaan, tetapi juga akan menutupi kondisi kesalahan lain yang mungkin belum Anda antisipasi, tetapi mungkin ingin ditangani dengan cara lain.

Apa yang membuat ini sangat penting dalam Python adalah bahwa dengan idiom bahasa ini, pengecualian tidak selalu kesalahan . Mereka sering digunakan dengan cara ini, tentu saja, seperti dalam kebanyakan bahasa. Tapi Python secara khusus kadang-kadang menggunakannya untuk mengimplementasikan jalur keluar alternatif dari beberapa tugas kode yang tidak benar-benar bagian dari kasus berjalan normal, tetapi masih diketahui muncul dari waktu ke waktu dan bahkan mungkin diharapkan dalam kebanyakan kasus. SystemExittelah disebutkan sebagai contoh lama, tetapi contoh paling umum saat ini mungkin StopIteration. Menggunakan pengecualian dengan cara ini menyebabkan banyak kontroversi, terutama ketika iterator dan generator pertama kali diperkenalkan ke Python, tetapi akhirnya gagasan itu menang.

Spooniest
sumber
12

Alasan # 1 telah dinyatakan - menyembunyikan kesalahan yang tidak Anda harapkan.

(# 2) - Ini membuat kode Anda sulit dibaca dan dipahami orang lain. Jika Anda menangkap FileNotFoundException ketika Anda mencoba membaca file, maka cukup jelas bagi pengembang lain apa fungsi yang harus dimiliki oleh blok 'catch'. Jika Anda tidak menentukan pengecualian, maka Anda perlu komentar tambahan untuk menjelaskan apa yang harus dilakukan oleh blok.

(# 3) - Ini menunjukkan pemrograman malas. Jika Anda menggunakan try / catch generik, ini menunjukkan bahwa Anda tidak memahami kemungkinan kesalahan run-time dalam program Anda, atau bahwa Anda tidak tahu pengecualian apa yang dimungkinkan dalam Python. Menangkap kesalahan spesifik menunjukkan bahwa Anda memahami program dan rentang kesalahan yang dilemparkan Python. Ini lebih cenderung membuat pengembang lain dan peninjau kode mempercayai pekerjaan Anda.

Kevin
sumber
12

Jadi, output apa yang dihasilkan kode ini?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

Sekarang bayangkan try- exceptblok adalah ratusan baris panggilan ke hierarki objek yang kompleks, dan itu sendiri disebut di tengah pohon panggilan program besar. Ketika programnya salah, di mana Anda mulai mencari?

Ian Harvey
sumber
5
Er, terima kasih kepada orang-orang yang 'memperbaiki' ini, tapi tolong jangan - ini sengaja salah, dalam arti 'pertanyaan wawancara'. Mungkin lebih halus saat muncul pertama kali - coba saja. Maksud saya adalah memeras pengecualian 'semua', terutama dalam Python, membuat debug sulit, bahkan dalam selusin baris kode sepele.
Ian Harvey
11

Secara umum, Anda dapat mengklasifikasikan kesalahan / pengecualian dalam salah satu dari tiga kategori :

  • Fatal : Bukan salahmu, kamu tidak bisa mencegahnya, kamu tidak bisa pulih darinya. Anda tentu tidak boleh mengabaikannya dan melanjutkan, dan meninggalkan program Anda dalam keadaan yang tidak diketahui. Biarkan saja kesalahan menghentikan program Anda, tidak ada yang dapat Anda lakukan.

  • Boneheaded : Kesalahan Anda sendiri, kemungkinan besar karena kesalahan pengawasan, bug, atau pemrograman. Anda harus memperbaiki bug. Sekali lagi, Anda tentu tidak boleh mengabaikan dan melanjutkan.

  • Eksogen : Anda dapat mengharapkan kesalahan ini dalam situasi luar biasa, seperti file tidak ditemukan atau koneksi terputus . Anda harus secara eksplisit menangani kesalahan ini, dan hanya ini.

Dalam semua kasus except: passhanya akan meninggalkan program Anda dalam keadaan yang tidak diketahui, di mana ia dapat menyebabkan lebih banyak kerusakan.

Daniel AA Pelsmaeker
sumber
6

Sederhananya, jika pengecualian atau kesalahan dilemparkan, ada sesuatu yang salah. Ini mungkin bukan sesuatu yang sangat salah, tetapi membuat, melempar, dan menangkap kesalahan dan pengecualian hanya demi menggunakan pernyataan goto bukanlah ide yang baik, dan itu jarang dilakukan. 99% dari waktu, ada masalah di suatu tempat.

Masalah perlu diatasi. Seperti halnya dalam kehidupan, dalam pemrograman, jika Anda membiarkan masalah sendirian dan mencoba mengabaikannya, masalah itu tidak hilang begitu saja; alih-alih mereka menjadi lebih besar dan berlipat ganda. Untuk mencegah masalah tumbuh pada Anda dan menyerang lagi lebih jauh di jalan, Anda 1) menghilangkannya dan membersihkan kekacauan sesudahnya, atau 2) mengatasinya dan membersihkan kekacauan sesudahnya.

Mengabaikan pengecualian dan kesalahan dan membiarkannya seperti itu adalah cara yang baik untuk mengalami kebocoran memori, koneksi basis data yang luar biasa, kunci yang tidak perlu pada izin file, dll.

Pada kesempatan yang jarang, masalahnya sangat kecil, sepele, dan - selain perlu dicoba ... catch block - mandiri , sehingga benar-benar tidak ada kekacauan yang harus dibersihkan setelahnya. Ini adalah satu-satunya kesempatan ketika praktik terbaik ini tidak selalu berlaku. Dalam pengalaman saya, ini secara umum berarti bahwa apa pun yang dilakukan kode pada dasarnya kecil dan tidak dapat dilupakan, dan sesuatu seperti coba lagi upaya atau pesan khusus tidak sebanding dengan kompleksitas maupun menahan utas.

Di perusahaan saya, aturannya adalah hampir selalu melakukan sesuatu di blok penangkap, dan jika Anda tidak melakukan apa pun, maka Anda harus selalu memberikan komentar dengan alasan yang sangat bagus mengapa tidak. Anda tidak boleh melewati atau meninggalkan blok penangkap kosong ketika ada sesuatu yang harus dilakukan.

Panzercrisis
sumber
6

Menurut pendapat saya kesalahan memiliki alasan untuk muncul, bahwa suara saya bodoh, tetapi memang begitulah adanya. Pemrograman yang baik hanya memunculkan kesalahan saat Anda harus menanganinya. Juga, seperti yang saya baca beberapa waktu lalu, "pass-Statement adalah Pernyataan yang menunjukkan kode yang akan dimasukkan nanti", jadi jika Anda ingin memiliki pernyataan-kecuali yang kosong jangan ragu untuk melakukannya, tetapi untuk program yang bagus akan ada menjadi bagian yang hilang. karena Anda tidak menangani hal-hal yang seharusnya Anda miliki. Munculnya pengecualian memberi Anda kesempatan untuk memperbaiki data input atau mengubah struktur data Anda sehingga pengecualian ini tidak terjadi lagi (tetapi dalam kebanyakan kasus (Pengecualian jaringan, Pengecualian masukan umum) pengecualian menunjukkan bahwa bagian selanjutnya dari program tidak akan berjalan dengan baik. Misalnya NetworkException dapat menunjukkan koneksi jaringan yang rusak dan program tidak dapat mengirim / menerima data pada langkah program selanjutnya.

Tetapi menggunakan pass block untuk hanya satu blok eksekusi adalah valid, karena Anda masih membedakan antara tipe pengecualian, jadi jika Anda meletakkan semua blok pengecualian dalam satu, itu tidak kosong:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

dapat ditulis ulang seperti itu:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

Jadi, bahkan beberapa blok kecuali dengan pernyataan lulus dapat menghasilkan kode, yang strukturnya menangani jenis pengecualian khusus.

Sirac
sumber
4

Semua komentar yang diajukan sejauh ini valid. Jika memungkinkan, Anda perlu menentukan pengecualian apa yang ingin Anda abaikan. Jika memungkinkan, Anda perlu menganalisis apa yang menyebabkan pengecualian, dan hanya mengabaikan apa yang ingin Anda abaikan, dan bukan sisanya. Jika pengecualian menyebabkan aplikasi "macet secara spektakuler", maka itulah, karena jauh lebih penting untuk mengetahui hal yang tidak terduga terjadi ketika itu terjadi, daripada menyembunyikan bahwa masalahnya pernah terjadi.

Dengan semua yang dikatakan, jangan mengambil praktik pemrograman sebagai yang terpenting. Ini bodoh. Selalu ada waktu dan tempat untuk melakukan blok abaikan-semua-pengecualian.

Contoh penting lainnya yang idiot adalah penggunaan gotooperator. Ketika saya masih di sekolah, profesor kami mengajari kami gotooperator hanya untuk menyebutkan bahwa Anda tidak boleh menggunakannya, PERNAH. Jangan percaya orang mengatakan kepada Anda bahwa xyz seharusnya tidak pernah digunakan dan tidak mungkin ada skenario ketika itu berguna. Selalu ada.

galet
sumber
1
Kasus "goto" adalah gaya dan masalah pendapat, sedangkan "kecuali: lulus" biasanya secara faktual salah. Ini mengasumsikan bahwa jika seseorang, misalnya, "membunuh -TERM" proses Anda pada saat itu maka ia harus mengabaikannya. Paling tidak itu perilaku buruk.
Score_Under
1
@Score_Di bawah namun ada kasus di mana ini sesuai untuk digunakan. Misalnya ketika fungsi yang Anda panggil bersifat pelengkap, yang tidak diketahui asalnya / penulis, tidak memengaruhi fungsionalitas inti, tetapi jika kerusakan dapat menyebabkan masalah. Saya sadar Anda akan berpendapat bahwa panggilan seperti itu harus diteliti dan dianalisis dengan benar, tetapi dalam kehidupan nyata itu tidak selalu mungkin.
Galet
Namun, jika saya ingin menghentikan proses Anda, kill -9 seharusnya bukan satu-satunya pilihan yang dapat diandalkan.
Score_Di Bawah
2

Menangani kesalahan sangat penting dalam pemrograman. Anda harus menunjukkan kepada pengguna apa yang salah. Dalam beberapa kasus Anda dapat mengabaikan kesalahan. Ini adalah praktik pemrograman yang sangat buruk.

fastcodejava
sumber
2

Karena belum disebutkan, itu gaya yang lebih baik untuk digunakan contextlib.suppress:

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

Perhatikan bahwa dalam contoh yang diberikan, status program tetap sama, terlepas dari tidaknya pengecualian terjadi. Artinya, somefile.tmpselalu menjadi tidak ada.

Mateen Ulhaq
sumber