Pertanyaan Secara manual meningkatkan (melempar) pengecualian dengan Python


Bagaimana saya bisa meningkatkan pengecualian dengan Python sehingga nanti bisa ditangkap melalui except blok?


1516
2018-01-12 21:07


asal


Jawaban:


Bagaimana cara membuang / menaikkan pengecualian secara manual dengan Python?

Gunakan konstruktor Exception yang paling spesifik yang semantis sesuai dengan masalah Anda.

Harus spesifik dalam pesan Anda, misalnya:

raise ValueError('A very specific bad thing happened.')

Jangan menaikkan pengecualian umum

Hindari mengajukan Pengecualian umum. Untuk menangkapnya, Anda harus menangkap semua pengecualian lain yang lebih spesifik yang meng-subclass-nya.

Masalah 1: Menyembunyikan bug

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Sebagai contoh:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Masalah 2: Tidak akan tertangkap

dan tangkapan yang lebih spesifik tidak akan menangkap pengecualian umum:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Praktik terbaik: raise pernyataan

Sebaliknya, gunakan konstruktor Exception yang paling spesifik yang semantis sesuai dengan masalah Anda.

raise ValueError('A very specific bad thing happened')

yang juga dengan mudah memungkinkan sejumlah argumen acak untuk diteruskan ke konstruktor:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Argumen ini diakses oleh args atribut pada objek Exception. Sebagai contoh:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

cetakan

('message', 'foo', 'bar', 'baz')    

Dalam Python 2.5, yang sebenarnya message atribut ditambahkan ke BaseException yang mendukung mendorong pengguna ke subkelas Pengecualian dan berhenti menggunakan args, tapi perkenalan message dan penghentian asli argumen telah dicabut.

Praktik terbaik: except ayat

Ketika berada di dalam kecuali klausa, Anda mungkin ingin, misalnya, mencatat bahwa jenis kesalahan tertentu terjadi, dan kemudian bangkit kembali. Cara terbaik untuk melakukan ini sambil mempertahankan jejak stack adalah dengan menggunakan pernyataan kenaikan kosong. Sebagai contoh:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Jangan modifikasi kesalahan Anda ... tetapi jika Anda bersikeras.

Anda dapat menyimpan stacktrace (dan nilai kesalahan) dengan sys.exc_info(), tapi ini jauh lebih rentan kesalahan dan memiliki masalah kompatibilitas antara Python 2 dan 3, lebih suka menggunakan telanjang raise untuk mengangkat kembali.

Untuk menjelaskan - sys.exc_info() mengembalikan tipe, nilai, dan traceback.

type, value, traceback = sys.exc_info()

Ini adalah sintaks dalam Python 2 - perhatikan ini tidak kompatibel dengan Python 3:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

Jika Anda mau, Anda dapat mengubah apa yang terjadi dengan peningkatan baru Anda - mis. pengaturan argumen baru untuk instance:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

Dan kami telah melestarikan seluruh traceback saat memodifikasi argumen. Perhatikan bahwa ini bukan praktik terbaik dan itu sintaks tidak valid dengan Python 3 (membuat kompatibilitas jauh lebih sulit untuk dikerjakan).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

Di Python 3:

    raise error.with_traceback(sys.exc_info()[2])

Sekali lagi: hindari memanipulasi tracebacks secara manual. Nya kurang efisien dan lebih banyak kesalahan rawan. Dan jika Anda menggunakan threading dan sys.exc_info Anda bahkan bisa mendapatkan traceback yang salah (terutama jika Anda menggunakan penanganan eksepsi untuk aliran kontrol - yang saya pribadi cenderung hindari.)

Python 3, Chaining pengecualian

Dengan Python 3, Anda dapat melakukan rantai Exceptions, yang mempertahankan tracebacks:

    raise RuntimeError('specific message') from error

Waspadalah:

  • ini tidak memungkinkan mengubah jenis kesalahan yang diajukan, dan
  • ini adalah tidak kompatibel dengan Python 2.

Metode Yang Dibatalkan:

Ini dapat dengan mudah disembunyikan dan bahkan masuk ke kode produksi. Anda ingin meningkatkan pengecualian, dan melakukannya akan meningkatkan pengecualian, tapi bukan yang dimaksudkan!

Berlaku dalam Python 2, tetapi tidak dengan Python 3 adalah sebagai berikut:

raise ValueError, 'message' # Don't do this, it's deprecated!

Hanya valid dalam banyak versi lama Python (2,4 dan lebih rendah), Anda mungkin masih melihat orang-orang meningkatkan string:

raise 'message' # really really wrong. don't do this.

Dalam semua versi modern, ini benar-benar akan meningkatkan TypeError, karena Anda tidak menaikkan tipe BaseException. Jika Anda tidak memeriksa pengecualian yang tepat dan tidak memiliki reviewer yang menyadari masalah ini, itu bisa masuk ke dalam produksi.

Contoh Penggunaan

Saya menaikkan Pengecualian untuk memperingatkan konsumen tentang API saya jika mereka menggunakannya dengan tidak benar:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Buat jenis kesalahan Anda sendiri ketika berhemat

"Aku ingin membuat kesalahan dengan sengaja, sehingga itu akan masuk ke dalam kecuali"

Anda dapat membuat jenis kesalahan Anda sendiri, jika Anda ingin menunjukkan sesuatu yang spesifik salah dengan aplikasi Anda, cukup subkelas titik yang sesuai dalam hierarki pengecualian:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

dan penggunaan:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')

1890
2018-06-05 16:30



JANGAN LAKUKAN INI. Budidaya telanjang Exception benar-benar tidak hal yang benar untuk dilakukan; Lihat Jawaban Aaron Hall yang luar biasa sebagai gantinya.

Tidak bisa jauh lebih pythonic dari ini:

raise Exception("I know python!")

Lihat dokumen pernyataan kenaikan gaji untuk python jika Anda ingin info lebih lanjut.


541
2018-01-12 21:08



Untuk kasus umum di mana Anda perlu membuang pengecualian dalam menanggapi beberapa kondisi yang tidak terduga, dan bahwa Anda tidak pernah berniat untuk menangkap, tetapi hanya gagal cepat untuk memungkinkan Anda untuk debug dari sana jika itu pernah terjadi - yang paling logis tampaknya AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)

29
2018-05-19 04:55



Dalam Python3 ada 4 syntaxes yang berbeda untuk pengecualian rasing:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. meningkatkan pengecualian vs. 2. meningkatkan pengecualian (args)

Jika Anda menggunakan raise exception (args)  untuk meningkatkan pengecualian maka args akan dicetak saat Anda mencetak objek pengecualian - seperti yang ditunjukkan pada contoh di bawah ini.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3. meningkatkan

raise pernyataan tanpa argumen apa pun akan memunculkan kembali pengecualian terakhir. Ini berguna jika Anda perlu melakukan beberapa tindakan setelah menangkap pengecualian dan kemudian ingin membesarkannya kembali. Tetapi jika tidak ada pengecualian sebelumnya, raise pernyataan menimbulkan TypeError Pengecualian.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. meningkatkan pengecualian (args) dari original_exception

Pernyataan ini digunakan untuk membuat pengecualian chaining di mana pengecualian yang dimunculkan sebagai tanggapan terhadap pengecualian lain dapat berisi detail dari pengecualian asli - seperti yang ditunjukkan pada contoh di bawah ini.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Keluaran:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero

22
2017-11-08 17:54



Baca jawaban yang ada terlebih dahulu, ini hanya sebuah tambahan.

Perhatikan bahwa Anda dapat menaikkan pengecualian dengan atau tanpa argumen.

Contoh:

raise SystemExit

keluar dari program tetapi Anda mungkin ingin tahu apa yang terjadi. Jadi Anda dapat menggunakan ini.

raise SystemExit("program exited")

ini akan mencetak "program keluar" ke stderr sebelum menutup program.


5
2018-03-29 11:59