# 1.1 異常是什麼 python使用異常對象來表示異常狀態,併在遇到錯誤時引發異常。異常對象未被處理,程式將終止並顯示一條錯誤信息。 我們可以通過各種方法引發和捕獲錯誤,並採取對應措施。 # 1.2 將“錯誤”變成異常 自主地引發異常 ## 1.2.1 raise語句 我們通過預測異常可能發生的 ...
1.1 異常是什麼
python使用異常對象來表示異常狀態,併在遇到錯誤時引發異常。異常對象未被處理,程式將終止並顯示一條錯誤信息。
我們可以通過各種方法引發和捕獲錯誤,並採取對應措施。
1.2 將“錯誤”變成異常
自主地引發異常
1.2.1 raise語句
我們通過預測異常可能發生的位置,通過raise語句主動拋出異常,用except語句來接收前面出現的異常,並作出對應的操作
def divide(x, y):
if y == 0:
# 引出異常
raise ZeroDivisionError("Division by zero!")
else:
return x / y
# 測試
try:
result = divide(6, 0)
# 接收異常
except ZeroDivisionError as ex:
print(f"Error: {str(ex)}")
else:
print(result)
> Error: Division by zero!
一些常見的內置異常類
Exception # 幾乎所有異常類均由這個派生而來
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x
IOError 輸入/輸出異常;基本上是無法打開文件
ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典里不存在的鍵
KeyboardInterrupt Ctrl+C被按下
NameError 使用一個還未被賦予對象的變數
SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了)
TypeError 傳入對象類型與要求的不符合
1.3 自定義的異常類
首先,要直接或間接地繼承Exception
類
格式
class 異常類名(Exception):
pass
利用自定義的異常類對1.2中的例子進行改進
class MyException(Exception): # 自定義異常類
pass
def divide(x, y):
if y == 0:
raise MyException("Division by zero!")
else:
return x / y
# 測試
try:
result = divide(6, 0)
except MyException as ex:
print(f"Error: {str(ex)}")
else:
print(result)
1.3.1一個具體使用場景
檢查用戶輸入的郵件格式,不對則報錯
class InvalidEmailException(Exception):
def __init__(self, email):
self.email = email
self.message = f"{email} is not a valid email address. Please try again."
super().__init__(self.message)
def send_email(to, subject, body):
if "@" not in to:
raise InvalidEmailException(to) # 拋出異常
print(f"Email sent to {to} with subject '{subject}' and body '{body}'.")
# 測試
try:
send_email("invalid-email", "Test email", "This is a test email.")
except InvalidEmailException as ex:
print(f"Error: {ex.message}")
第五行super().__init__(self.message)
調用基類 Exception
的構造函數來初始化 self.message
。
Exception的構造函數:
class Exception(BaseException):
def __init__(self, *args: object) -> None:
"""
Initialize self. See help(type(self)) for accurate signature.
"""
pass
構造函數接受可變長度的參數 *args
,但是它並沒有執行任何實際操作,只是一個空的 pass
語句。這意味著,我們可以在自定義異常類的構造函數中調用父類 Exception
的構造函數,同時傳遞自定義的錯誤消息作為參數。
關於第五行的作用:
-
第5行的作用只是使用基類
Exception
的構造函數,來完成可能會影響異常的某些其他行為,如設置異常的堆棧跟蹤信息等。這些信息可以幫助程式員更輕鬆地找到代碼中發生異常的位置。因此,雖然第5行實際上不是非常必要,但它是一個良好的實踐,可以幫助進一步豐富異常信息。
-
個人認為,可以通過重寫Exception的構造函數
-
猜想:可能會定義多個自定義類,通過定義Exception參數的方式,進行類之間的異常信息傳遞
1.3.2 同時監測多種異常
將異常類型以元組形式展現(沒有採用raise,等待程式拋出異常並接收)
class CustomException(Exception):
def __init__(self, message):
super().__init__(message) # 與上個程式第五行同理
# 若出現異常,則要求用戶重新輸入
while True:
try:
# 獲取用戶輸入的除數和被除數
divisor = int(input("Enter the divisor: "))
dividend = int(input("Enter the dividend: "))
result = dividend / divisor
# 通過元組進行多種異常監測
except (ZeroDivisionError, TypeError, ValueError) as ex:
# 捕獲多種異常
print(ex)
else:
print(f"The result of division is: {result}")
break # 成功即跳出迴圈
運行結果
Enter the divisor: 0
Enter the dividend: 1
division by zero
Enter the divisor: we
invalid literal for int() with base 10: 'we'
Enter the divisor: 2
Enter the dividend: 1
The result of division is: 0.5
1.3.3 一網打盡的異常和else
同時監測多個異常可能不夠,可以一網打盡
# 若出現異常,則要求用戶重新輸入
while True:
try:
# 獲取用戶輸入的除數和被除數
divisor = int(input("Enter the divisor: "))
dividend = int(input("Enter the dividend: "))
result = dividend / divisor
# 通過元組進行多種異常監測
except Exception as ex:
#對所有Exception的子類異常進行監測,只有發生了對應的子類異常,才會被捕獲
# 捕獲多種異常
print(ex)
else: # 通過else語句實現迴圈,這裡是except語句的else, 當不執行except語句時,執行else
print(f"The result of division is: {result}")
break # 成功即跳出迴圈
運行結果
Enter the divisor: er
invalid literal for int() with base 10: 'er'
Enter the divisor: 0
Enter the dividend: 1
division by zero
Enter the divisor: 1
Enter the dividend: 2
The result of division is: 2.0
tip:
在編寫代碼時,最好不要捕獲所有異常類型。我們應該儘可能地特定地捕獲那些預期的、已知的異常類型,並將其他異常類型傳遞給更高層的異常處理機制進行處理。這樣可以更加有效地調試和解決問題,而且代碼更加可讀和可維護。
1.3.4 最後的finally
無論是否發生異常,finally語句均會運行。多用於執行清理工作。
如:關閉文件,關閉網路套接字等
try:
1 / 2
except NameError:
print("Unknown wariable")
else:
print('That went well')
finally:
print('Cleaning up.')
運行結果
That went well
Cleaning up.
1.3.5 異常的傳遞
如果不處理函數中引發的異常,它將向上傳播到調用函數中,直到主程式,若主程式中還是不能處理異常,程式將通知並顯示站跟蹤信息。
def faulty():
raise Exception("wrong")
def ig_exception():
faulty()
def hl_exception():
try:
faulty()
except:
print('Exception handled')
ig_exception()
運行結果:列印了棧跟蹤信息和異常信息
Traceback (most recent call last):
File "d:\M\github\Python\Demo\t12.py", line 12, in <module>
ig_exception()
File "d:\M\github\Python\Demo\t12.py", line 5, in ig_exception
faulty()
File "d:\M\github\Python\Demo\t12.py", line 2, in faulty
raise Exception("wrong")
Exception: wrong
若是只調用hl_exception()
異常會被處理,程式不會終止
hl_exception()
print("hello") # 列印出結果,說明程式未終止,仍在運行
> Exception handled
> hello
1.4 異常之禪
- 除了使用try/except語句來處理異常外,還可以使用if/else語句,只是不推薦這樣做
- 可以檢查對象是否包含特定的屬性
try:
obj.write
except AttributeError:
print('The object is not worteable')
else:
print('The object is writeable')
- 不那麼異常的時候,可以發出警告,由模塊
warning
中的函數warn提供
from warnings import warn
warn('got a bad feeling.')
print('hello') # 可以列印,說明程式仍在運行
>
d:\M\github\Python\Demo\t12.py:2: UserWarning: got a bad feeling.
warn('got a bad feeling.')
hello