[英]Manually raising (throwing) an exception in Python
如何在 Python 中引發異常,以便以后可以通過except
塊捕獲它?
如何在 Python 中手動拋出/引發異常?
使用語義上適合您問題的最具體的 Exception 構造函數。
在您的信息中具體說明,例如:
raise ValueError('A very specific bad thing happened.')
避免引發泛型Exception
。 要捕獲它,您必須捕獲所有其他將其子類化的更具體的異常。
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
例如:
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',)
更具體的捕獲不會捕獲一般異常:
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
raise
聲明相反,請使用語義上適合您問題的最具體的 Exception 構造函數。
raise ValueError('A very specific bad thing happened')
這也方便地允許將任意數量的參數傳遞給構造函數:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
這些參數由Exception
對象的args
屬性訪問。 例如:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
印刷
('message', 'foo', 'bar', 'baz')
在 Python 2.5 中,向BaseException
添加了一個實際的message
屬性,以鼓勵用戶將 Exceptions 子類化並停止使用args
,但引入message
和最初棄用 args 已被撤回。
except
子句例如,當在 except 子句中時,您可能希望記錄發生了特定類型的錯誤,然后重新引發。 在保留堆棧跟蹤的同時執行此操作的最佳方法是使用裸 raise 語句。 例如:
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!
您可以使用sys.exc_info()
保留堆棧跟蹤(和錯誤值),但這更容易出錯並且在 Python 2 和 3 之間存在兼容性問題,更喜歡使用裸raise
來重新引發。
解釋一下 - sys.exc_info()
返回類型、值和回溯。
type, value, traceback = sys.exc_info()
這是 Python 2 中的語法 - 請注意,這與 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]
如果你願意,你可以修改你的新 raise 會發生什么 - 例如為實例設置新的args
:
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
我們在修改 args 時保留了整個回溯。 請注意,這不是最佳實踐,並且在 Python 3 中是無效的語法(使得保持兼容性變得更加困難)。
>>> 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>
在Python 3中:
raise error.with_traceback(sys.exc_info()[2])
再次:避免手動操作回溯。 它的效率較低且更容易出錯。 如果你使用線程和sys.exc_info
你甚至可能得到錯誤的回溯(特別是如果你對控制流使用異常處理——我個人傾向於避免這種情況。)
在 Python 3 中,您可以鏈接異常,以保留回溯:
raise RuntimeError('specific message') from error
意識到:
這些可以很容易地隱藏甚至進入生產代碼。 您想引發異常,並且這樣做會引發異常,但不是預期的!
在 Python 2 中有效,但在 Python 3中無效的如下:
raise ValueError, 'message' # Don't do this, it's deprecated!
僅在更舊版本的 Python (2.4 及更低版本)中有效,您可能仍會看到人們提出字符串:
raise 'message' # really really wrong. don't do this.
在所有現代版本中,這實際上會引發TypeError
,因為您沒有引發BaseException
類型。 如果您沒有檢查正確的異常並且沒有意識到該問題的審閱者,它可能會投入生產。
如果他們使用不正確,我會引發異常來警告消費者我的 API:
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)))
“我想故意犯錯誤,這樣它就會進入例外”
您可以創建自己的錯誤類型,如果您想指出您的應用程序有特定的錯誤,只需將異常層次結構中的適當點子類化:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
和用法:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
不要這樣做。 提出一個簡單的
Exception
絕對不是正確的做法; 請參閱Aaron Hall 的出色答案。
沒有比這更 Pythonic 的了:
raise Exception("I know Python!")
將Exception
替換為您要拋出的特定類型的異常。
如果您想了解更多信息,請參閱 Python 的 raise 語句文檔。
在 Python 3 中有四種不同的引發異常的語法:
如果您使用raise exception (args)
來引發異常,則在您打印異常對象時將打印args
- 如下例所示。
# 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 exception
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
不帶任何參數的raise
語句會重新引發最后一個異常。
如果您需要在捕獲異常后執行一些操作然后想要重新引發它,這很有用。 但是如果之前沒有任何異常, raise
語句會引發TypeError
異常。
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
此語句用於創建異常鏈接,其中響應另一個異常而引發的異常可以包含原始異常的詳細信息 - 如下例所示。
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__)
輸出:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero
對於您需要拋出異常以響應某些意外情況的常見情況,並且您從不打算捕獲,而只是快速失敗以使您能夠從那里進行調試,如果它發生了 - 最合乎邏輯的一個似乎是AssertionError
:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
首先閱讀現有答案,這只是一個附錄。
請注意,您可以使用或不使用參數引發異常。
例子:
raise SystemExit
退出程序,但您可能想知道發生了什么。 所以你可以使用這個。
raise SystemExit("program exited")
這將在關閉程序之前將“程序退出”打印到標准錯誤。
請注意:有時您確實想要處理通用異常。 如果您正在處理一堆文件並記錄錯誤,您可能希望捕獲文件發生的任何錯誤,記錄它,然后繼續處理其余文件。 在這種情況下,一個
try:
foo()
except Exception as e:
print(e) # Print out handled error
塊是一個很好的方法。 不過,您仍然需要raise
特定的異常,以便了解它們的含義。
要捕獲所有異常,請使用BaseException
。 它位於異常層次結構的頂部。
try:
# Do something
except BaseException as error:
print('An exception occurred: {}'.format(error))
try:
# Do something
except BaseException as error:
raise 'An exception occurred: {}'.format(error)
以上代碼支持 Python 2.7 到最新版本。
參考: 異常層次結構
為此,您應該學習 Python 的raise語句。
它應該保存在 try 塊內。
例子 -
try:
raise TypeError # Replace TypeError by any other error if you want
except TypeError:
print('TypeError raised')
您可能還想引發自定義異常。 例如,如果您正在編寫一個庫,那么為您的模塊創建一個基本異常類,然后讓自定義子異常更加具體,這是一個非常好的做法。
你可以這樣實現:
class MyModuleBaseClass(Exception):
pass
class MoreSpecificException(MyModuleBaseClass):
pass
# To raise custom exceptions, you can just
# use the raise keyword
raise MoreSpecificException
raise MoreSpecificException('message')
如果您對自定義基類不感興趣,您可以從普通異常類(如Exception
、 TypeError
、 ValueError
等)繼承自定義異常類。
如果您不關心要引發哪個錯誤,則可以使用assert
引發AssertionError
:
>>> assert False, "Manually raised error"
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
assert False, "Manually raised error"
AssertionError: Manually raised error
>>>
如果條件為False
,則assert
關鍵字會引發AssertionError
。 在這種情況下,我們直接指定了False
,所以它會引發錯誤,但是要讓它有一個我們希望它引發的文本,我們添加一個逗號並指定我們想要的錯誤文本。 在這種情況下,我寫了Manually raised error
,並用該文本引發了它。
如果您不關心引發的異常,請執行以下操作:
def crash(): return 0/0
好舊的除以 0。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.