1. 什么是錯誤和異常
在Python中,錯誤可以分為兩類:
- 語法錯誤(Syntax Errors):代碼不符合Python語法規則
- 異常(Exceptions):語法正確的代碼在運行時發生的錯誤
# 語法錯誤示例
print("Hello World" # 缺少右括號# 異常示例
print(10 / 0) # ZeroDivisionError: division by zero
2. 常見的異常類型
Python內置了許多異常類型,常見的有:
# NameError - 嘗試訪問未定義的變量
print(undefined_variable)# TypeError - 類型操作錯誤
"2" + 2 # 不能將字符串和整數相加# IndexError - 索引超出范圍
lst = [1, 2, 3]
print(lst[3](@ref)# KeyError - 字典鍵不存在
d = {'a': 1}
print(d['b'])# FileNotFoundError - 文件不存在
open('nonexistent.txt')
3. 異常處理:try-except語句
使用try-except可以捕獲并處理異常:
try:# 可能引發異常的代碼result = 10 / 0
except ZeroDivisionError:# 處理特定異常print("不能除以零!")
捕獲多種異常
try:num = int(input("請輸入一個數字: "))result = 100 / numprint("結果是:", result)
except ValueError:print("輸入的不是有效數字!")
except ZeroDivisionError:print("不能輸入零!")
except Exception as e: # 捕獲所有其他異常print(f"發生未知錯誤: {e}")
4. try-except-else-finally完整結構
try:file = open('example.txt', 'r')content = file.read()
except FileNotFoundError:print("文件不存在!")
else:# 如果沒有異常發生,執行else塊print("文件內容:", content)
finally:# 無論是否發生異常都會執行print("清理工作...")if 'file' in locals() and not file.closed:file.close()
5. 拋出異常:raise語句
我們可以主動拋出異常:
def check_age(age):if age < 0:raise ValueError("年齡不能為負數")elif age < 18:raise ValueError("未成年禁止訪問")else:print("歡迎訪問")try:check_age(-5)
except ValueError as e:print(f"錯誤: {e}")
6. 自定義異常
我們可以創建自己的異常類型:
class MyCustomError(Exception):"""自定義異常類 - 用于演示特定業務場景的錯誤"""def __init__(self, message, error_code=500):self.message = messageself.error_code = error_codesuper().__init__(f"錯誤代碼 {error_code}: {message}")def __str__(self):"""自定義異常信息的字符串表示形式"""return f"[{self.__class__.__name__}] {self.message}"def calculate_division(dividend, divisor):"""演示函數:執行除法運算,當條件不滿足時拋出不同類型的異常"""if not isinstance(dividend, (int, float)) or not isinstance(divisor, (int, float)):raise TypeError("被除數和除數必須是數字類型")if divisor == 0:raise ZeroDivisionError("除數不能為零")if dividend < 0 or divisor < 0:# 拋出自定義異常:當輸入為負數時raise MyCustomError("計算不支持負數輸入", error_code=400)return dividend / divisordef main():"""主函數:演示異常的捕獲和處理流程"""print("=== 自定義異常演示程序 ===")while True:try:# 獲取用戶輸入num1 = float(input("\n請輸入被除數(輸入q退出):"))num2 = float(input("請輸入除數:"))# 執行計算result = calculate_division(num1, num2)print(f"計算結果:{num1} ÷ {num2} = {result}")except ValueError as e:# 處理非數字輸入if "q" in str(e).lower():print("程序已退出")breakprint(f"輸入錯誤:請輸入有效的數字。錯誤詳情:{e}")except ZeroDivisionError as e:# 處理內置異常print(f"數學錯誤:{e}")except MyCustomError as e:# 處理自定義異常print(f"業務錯誤:{e}(錯誤代碼:{e.error_code})")except TypeError as e:# 處理類型錯誤print(f"類型錯誤:{e}")except Exception as e:# 捕獲所有其他未預期的異常print(f"未知錯誤:{e}")breakif __name__ == "__main__":main()
?這個示例程序包含以下關鍵特性:
- 自定義異常類:
MyCustomError
繼承自Exception
,包含錯誤信息和錯誤代碼- 異常拋出函數:
calculate_division
根據不同條件拋出不同類型的異常- 多級異常捕獲:在
main
函數中使用多個except
塊分別處理不同類型的異常- 用戶交互:通過控制臺輸入演示異常的觸發和處理流程
你可以運行這段代碼并嘗試以下輸入場景:
- 正常輸入兩個正數(如
10
和2
)- 輸入零作為除數(觸發
ZeroDivisionError
)- 輸入負數(觸發
MyCustomError
)- 輸入非數字字符(觸發
ValueError
)- 輸入
q
退出程序
通過觀察不同輸入下的輸出結果,你可以深入理解自定義異常的工作機制和優勢。
7. 斷言assert
assert用于確保某個條件為真,否則拋出AssertionError:
def divide(a, b):assert b != 0, "除數不能為零"return a / bprint(divide(10, 2)) # 正常
print(divide(10, 0)) # 拋出AssertionError
8. 實際應用案例
案例1:處理用戶輸入
while True:try:age = int(input("請輸入您的年齡: "))if age < 0:raise ValueError("年齡不能為負數")breakexcept ValueError as e:print(f"無效輸入: {e}")print(f"您的年齡是: {age}")
案例2:文件操作
def read_file(filename):try:with open(filename, 'r') as file:return file.read()except FileNotFoundError:print(f"文件 {filename} 不存在")return Noneexcept IOError:print(f"讀取文件 {filename} 時發生錯誤")return Nonecontent = read_file('data.txt')
if content:print("文件內容:", content)
案例3:網絡請求
import requestsdef fetch_url(url):try:response = requests.get(url, timeout=5)response.raise_for_status() # 如果請求不成功,拋出HTTPErrorreturn response.textexcept requests.exceptions.Timeout:print("請求超時")except requests.exceptions.HTTPError as err:print(f"HTTP錯誤: {err}")except requests.exceptions.RequestException as err:print(f"請求錯誤: {err}")return Nonehtml = fetch_url("https://www.example.com")
if html:print("獲取內容成功!")
9. 異常處理的最佳實踐
- 1.不要過度使用try-except:只捕獲你知道如何處理的異常
- 2.盡量具體:捕獲特定異常而不是通用的Exception
- 3.記錄異常信息:使用logging模塊記錄異常詳情
- 4.保持簡潔:try塊中只包含可能引發異常的代碼
- 5.清理資源:使用finally或上下文管理器(with語句)確保資源釋放
import logginglogging.basicConfig(filename='app.log', level=logging.ERROR)def process_data(data):try:# 只包含可能引發異常的代碼result = complex_operation(data)except ValueError as e:logging.error(f"處理數據時發生值錯誤: {e}")return Noneexcept DatabaseError as e:logging.error(f"數據庫錯誤: {e}")return Noneelse:return result
10. 總結
異常處理是Python編程中非常重要的部分,合理使用異常處理可以使程序:
- 更加健壯,能夠處理意外情況
- 更易于調試和維護
- 提供更好的用戶體驗
異常處理不是用來隱藏錯誤的,而是為了優雅地處理錯誤情況!