python: Bagaimana saya tahu jenis pengecualian apa yang terjadi?

230

Saya memiliki fungsi yang dipanggil oleh program utama:

try:
    someFunction()
except:
    print "exception happened!"

tetapi di tengah pelaksanaan fungsi itu menimbulkan pengecualian, sehingga melompat ke exceptbagian tersebut.

Bagaimana saya bisa melihat dengan tepat apa yang terjadi di dalam someFunction()yang menyebabkan pengecualian terjadi?

Shang Wang
sumber
9
Tidak pernah menggunakan bare except:(tanpa bare raise), kecuali mungkin sekali per program, dan lebih baik tidak melakukannya.
Mike Graham
Jika Anda menggunakan beberapa exceptklausa, Anda tidak perlu memeriksa jenis pengecualian, itulah yang biasanya dilakukan untuk bertindak sesuai dengan jenis pengecualian tertentu.
Rik Poggi
3
Jika Anda peduli tentang jenis pengecualian, itu karena Anda telah mempertimbangkan jenis pengecualian apa yang mungkin terjadi secara logis.
Karl Knechtel
3
Di dalam exceptblok pengecualian tersedia melalui sys.exc_info()fungsi - fungsi ini mengembalikan tuple dari tiga nilai yang memberikan informasi tentang pengecualian yang saat ini sedang ditangani.
Piotr Dobrogost

Jawaban:

384

Semua jawaban lainnya menunjukkan bahwa Anda tidak boleh menangkap pengecualian umum, tetapi tampaknya tidak ada yang mau memberi tahu Anda alasannya, yang penting untuk dipahami ketika Anda dapat melanggar "aturan". Berikut ini penjelasannya. Pada dasarnya, agar Anda tidak menyembunyikan:

Jadi selama Anda berhati-hati untuk tidak melakukan hal-hal itu, tidak apa-apa untuk menangkap pengecualian umum. Misalnya, Anda dapat memberikan informasi tentang pengecualian kepada pengguna dengan cara lain, seperti:

  • Hadir pengecualian sebagai dialog dalam GUI
  • Mentransfer pengecualian dari utas atau proses pekerja ke utas atau proses pengontrol dalam aplikasi multithreading atau multiproses

Jadi bagaimana cara menangkap pengecualian umum? Ada beberapa cara. Jika Anda hanya ingin objek pengecualian, lakukan seperti ini:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Membuat yakin message dibawa ke perhatian pengguna dalam cara yang sulit untuk miss! Mencetaknya, seperti yang ditunjukkan di atas, mungkin tidak cukup jika pesan terkubur dalam banyak pesan lainnya. Gagal mendapatkan perhatian pengguna sama saja dengan menelan semua pengecualian, dan jika ada satu kesan yang seharusnya Anda dapatkan setelah membaca jawaban di halaman ini, itu karena ini bukan hal yang baik . Mengakhiri kecuali blok dengan araise pernyataan akan memperbaiki masalah dengan secara transparan mengembalikan pengecualian yang tertangkap.

Perbedaan antara di atas dan menggunakan hanya except:tanpa argumen ada dua:

  • Telanjang except:tidak memberi Anda objek pengecualian untuk diperiksa
  • Pengecualian SystemExit, KeyboardInterruptdan GeneratorExittidak ditangkap oleh kode di atas, yang umumnya Anda inginkan. Lihat hierarki pengecualian .

Jika Anda juga ingin stacktrace yang sama dengan yang Anda dapatkan jika Anda tidak menangkap pengecualian, Anda bisa mendapatkan yang seperti ini (masih di dalam klausa kecuali):

import traceback
print traceback.format_exc()

Jika Anda menggunakan loggingmodul, Anda dapat mencetak pengecualian ke log (bersama dengan pesan) seperti ini:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Jika Anda ingin menggali lebih dalam dan memeriksa tumpukan, lihat variabel dll, gunakan post_mortemfungsi pdbmodul di dalam blok kecuali:

import pdb
pdb.post_mortem()

Saya menemukan metode terakhir ini sangat berharga ketika memburu bug.

Lauritz V. Thaulow
sumber
1
traceback.print_exc () akan melakukan hal yang sama dengan "" .join-hal Anda yang lebih rumit, saya kira.
Gurgeh
1
@ Borgeh Ya, tapi saya tidak tahu apakah dia ingin mencetaknya atau menyimpannya ke file atau mencatatnya atau melakukan hal lain dengannya.
Lauritz V. Thaulow
Saya tidak memilih, tetapi saya mengatakan itu karena Anda seharusnya meletakkan lemak yang sangat besar di awal dengan mengatakan Anda tidak perlu semua ini, tapi begini caranya . Dan mungkin karena Anda menyarankan untuk menangkap Pengecualian generik.
Rik Poggi
10
@Rik Saya pikir Anda mungkin sangat membutuhkan semua ini. Misalnya, jika Anda memiliki program dengan GUI dan backend, dan Anda ingin menyajikan semua pengecualian dari backend sebagai pesan GUI alih-alih membiarkan program Anda berhenti dengan jejak stack. Dalam kasus seperti itu, Anda harus menangkap Pengecualian generik , membuat teks traceback untuk dialog, mencatat pengecualian juga, dan jika dalam mode debug, masukkan post-mortem.
Lauritz V. Thaulow
18
@RikPoggi: Berpikir naif. Ada banyak keadaan yang masuk akal ketika Anda perlu menangkap pengecualian dari kode orang lain dan Anda tidak tahu pengecualian apa yang akan muncul.
stackoverflowuser2010
63

Dapatkan nama kelas yang dimiliki objek pengecualian:

e.__class__.__name__

dan menggunakan fungsi print_exc () juga akan mencetak jejak tumpukan yang merupakan info penting untuk pesan kesalahan apa pun.

Seperti ini:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Anda akan mendapatkan output seperti ini:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

Dan setelah dicetak dan dianalisis, kode dapat memutuskan untuk tidak menangani pengecualian dan hanya menjalankan raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Keluaran:

special case of CustomException not interfering

Dan interpreter mencetak pengecualian:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Setelah raisepengecualian asli terus menyebar lebih jauh ke tumpukan panggilan. ( Waspadai kemungkinan jebakan ) Jika Anda meningkatkan pengecualian baru, ia mencari jejak tumpukan yang lebih pendek.

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Keluaran:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Perhatikan bagaimana traceback tidak menyertakan calculate()fungsi dari garis 9yang merupakan asal dari pengecualian asli e.

Alex
sumber
Jika Anda ingin menyimpan traceback sebagai string, Anda dapat menggunakannya traceback.format_exc()juga
Stevoisiak
1
e.__class__.__name__apakah ini sama dengan type(e).__name__, seperti yang disarankan oleh jawaban di atas?
information_interchange
1
@information_interchange ya. Konten pertanyaan dan jawaban yang diterima berubah sepenuhnya dari waktu ke waktu. Sayang peserta lain tidak diberi tahu oleh mesin SO :(
Alex
14

Anda biasanya tidak boleh menangkap semua pengecualian yang mungkin terjadi try: ... exceptkarena ini terlalu luas. Hanya menangkap mereka yang diharapkan terjadi karena alasan apa pun. Jika Anda benar-benar harus, misalnya jika Anda ingin mengetahui lebih lanjut tentang beberapa masalah saat debugging, Anda harus melakukannya

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.
hochl
sumber
17
Penggunaan kata "tidak pernah" di sini tidak pernah salah. Saya menggunakan try: ... except Exception:banyak hal, misalnya penggunaan perpustakaan yang tergantung pada jaringan, atau pemijat data yang mungkin mengirim barang aneh kepadanya. Secara alami saya juga memiliki logging yang benar. Ini sangat penting untuk memungkinkan program untuk terus beroperasi jika ada kesalahan tunggal dalam input data.
Lutut
3
Pernah mencoba menangkap semua pengecualian yang dapat dimunculkan saat mengirim email menggunakan smtplib?
linusg
1
Mungkin ada beberapa kasus khusus di mana menangkap semua pengecualian diperlukan, tetapi pada tingkat umum Anda hanya perlu menangkap apa yang Anda harapkan sehingga Anda tidak sengaja menyembunyikan kesalahan yang tidak Anda antisipasi. Penebangan yang baik juga merupakan ide yang bagus.
Hochl
1
Sangat masuk akal untuk menangkap semua pengecualian. Jika Anda memanggil perpustakaan pihak ketiga, Anda mungkin tidak tahu pengecualian apa yang akan muncul di perpustakaan itu. Dalam kasus seperti itu, satu-satunya jalan lain adalah menangkap semua pengecualian, misalnya mencatatnya dalam file.
stackoverflowuser2010
Ok ok Anda benar, saya akan ulangi jawaban saya untuk menjelaskan bahwa ada kasus penggunaan yang valid untuk menangkap semua.
Hochl
10

Kecuali jika somefunctionfungsi warisan kode yang sangat buruk, Anda seharusnya tidak memerlukan apa yang Anda minta.

Gunakan beberapa exceptklausa untuk menangani dengan cara yang berbeda pengecualian berbeda:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

Poin utama adalah bahwa Anda tidak harus menangkap pengecualian umum, tetapi hanya yang perlu. Saya yakin Anda tidak ingin membayangi kesalahan atau bug yang tidak terduga.

Rik Poggi
sumber
8
Jika Anda menggunakan perpustakaan pihak ketiga, Anda mungkin tidak tahu pengecualian apa yang akan muncul di dalamnya. Bagaimana Anda bisa menangkap mereka secara individual?
stackoverflowuser2010
8

Sebagian besar jawaban menunjuk ke except (…) as (…):sintaks (memang demikian) tetapi pada saat yang sama tidak ada yang ingin berbicara tentang gajah di dalam ruangan, di mana gajah sys.exc_info()berfungsi. Dari dokumentasi dari sys modul (penekanan):

Fungsi ini mengembalikan tupel dari tiga nilai yang memberikan informasi tentang pengecualian yang sedang ditangani.
(...)
Jika tidak ada pengecualian sedang ditangani di mana saja pada stack, sebuah tuple berisi tiga nilai None dikembalikan. Jika tidak, nilai yang dikembalikan adalah (tipe, nilai, traceback). Artinya adalah: tipe mendapat tipe pengecualian yang sedang ditangani (subkelas BaseException); nilai mendapatkan instance eksepsi (turunan dari tipe eksepsi); traceback mendapatkan objek traceback (lihat Manual Referensi) yang merangkum tumpukan panggilan pada titik di mana pengecualian awalnya terjadi.

Saya pikir yang sys.exc_info()bisa diperlakukan sebagai jawaban paling langsung untuk pertanyaan awal dari Bagaimana saya tahu jenis pengecualian apa yang terjadi?

Piotr Dobrogost
sumber
1
Itu jawaban yang tepat untuk saya karena itu memecahkan masalah pengecualian apa yang terjadi jadi apa yang harus saya masukkan bukannya telanjang except. Hanya demi kelengkapan, exctype, value = sys.exc_info()[:2]akan memberi tahu Anda jenis pengecualian yang kemudian dapat digunakan di Internet except.
Ondrej Burkert
5

coba: someFunction () kecuali Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))
wp-overwatch.com
sumber
-1 seperti menggunakan exc.__class__.__name__telah disarankan dalam jawaban Alex - stackoverflow.com/a/9824060/95735
Piotr Dobrogost
3

Jawaban-jawaban ini baik untuk debugging, tetapi untuk pemrograman pengecualian, isinstance(e, SomeException)bisa berguna, karena juga menguji subclass SomeException, sehingga Anda dapat membuat fungsionalitas yang berlaku untuk hierarki pengecualian.

Chris
sumber
1

Begini cara saya menangani pengecualian saya. Idenya adalah untuk mencoba menyelesaikan masalah jika itu mudah, dan kemudian tambahkan solusi yang lebih diinginkan jika memungkinkan. Jangan memecahkan masalah dalam kode yang menghasilkan pengecualian, atau bahwa kode kehilangan jejak dari algoritma asli, yang harus ditulis to-the-point. Namun, sampaikan data apa yang diperlukan untuk menyelesaikan masalah ini, dan kembalikan lambda jika Anda tidak dapat memecahkan masalah di luar kode yang menghasilkannya.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Untuk saat ini, karena saya tidak ingin berpikir secara berlawanan dengan tujuan aplikasi saya, saya belum menambahkan solusi yang rumit. Tetapi di masa depan, ketika saya tahu lebih banyak tentang solusi yang mungkin (karena aplikasi ini dirancang lebih), saya bisa menambahkan kamus solusi yang diindeks olehduring .

Dalam contoh yang ditunjukkan, satu solusi mungkin untuk mencari data aplikasi yang disimpan di tempat lain, katakanlah jika file 'app.p' terhapus secara tidak sengaja.

Untuk saat ini, karena menulis handler pengecualian bukanlah ide yang cerdas (kami belum tahu cara terbaik untuk menyelesaikannya, karena desain aplikasi akan berkembang), kami cukup mengembalikan perbaikan mudah yang bertindak seperti sedang berjalan aplikasi untuk pertama kalinya (dalam hal ini).

AbstractAlgebraLearner
sumber
0

Untuk menambah jawaban Lauritz, saya membuat dekorator / pembungkus untuk penanganan pengecualian dan log pembungkus yang jenis pengecualian terjadi.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Ini dapat dipanggil pada metode kelas atau fungsi mandiri dengan dekorator:

@general_function_handler

Lihat blog saya tentang untuk contoh lengkap: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/

rirwin
sumber
0

Anda bisa mulai seperti yang direkomendasikan Lauritz, dengan:

except Exception as ex:

dan kemudian print exseperti itu:

try:
    #your try code here
except Exception as ex:
    print ex
Gura
sumber
Bisakah Anda menguraikan sedikit sehingga jawaban Anda berdiri sendiri?
GHC
1
yakin: Anda dapat mencetak pengecualian yang tertangkap seperti ini: coba: # kode coba Anda di sini kecuali Pengecualian sebagai contoh: cetak ex sekarang kesalahan akan dicetak
Gura
-2

Pengecualian aktual dapat ditangkap dengan cara berikut:

try:
    i = 1/0
except Exception as e:
    print e

Anda dapat mempelajari lebih lanjut tentang pengecualian dari Tutorial Python .

Kevin Coffey
sumber
-2

Pertanyaan Anda adalah: "Bagaimana saya bisa melihat dengan tepat apa yang terjadi pada fungsi tertentu () yang menyebabkan pengecualian terjadi?"

Tampak bagi saya bahwa Anda tidak bertanya tentang bagaimana menangani pengecualian yang tidak terduga dalam kode produksi (seperti yang diasumsikan oleh banyak jawaban), tetapi bagaimana mencari tahu apa yang menyebabkan pengecualian tertentu selama pengembangan.

Cara termudah adalah dengan menggunakan debugger yang bisa berhenti ketika pengecualian yang tidak tertangkap terjadi, lebih disukai tidak keluar, sehingga Anda dapat memeriksa variabel. Sebagai contoh, PyDev di IDE open source Eclipse dapat melakukan itu. Untuk mengaktifkannya di Eclipse, buka perspektif Debug, pilih Manage Python Exception Breakpointsdi Runmenu, dan centang Suspend on uncaught exceptions.


sumber
-4

Hanya menahan diri dari menangkap pengecualian dan traceback yang dicetak Python akan memberi tahu Anda apa yang terjadi pengecualian.

Mike Graham
sumber