异常处理#
异常处理是 Python 中处理错误和异常情况的机制。良好的异常处理可以使程序更加健壮。
什么是异常?#
异常是程序运行时发生的错误。当 Python 遇到错误时,会抛出(raise)一个异常。
# 常见的异常
1 / 0 # ZeroDivisionError: division by zero
int("abc") # ValueError: invalid literal for int()
x[10] # IndexError: list index out of range
d["key"] # KeyError: 'key'(如果key不存在)try-except 语句#
基本语法#
try:
# 可能出错的代码
result = 10 / 0
except ZeroDivisionError:
# 处理异常
print("除零错误!")
# 程序继续执行
print("程序继续运行")捕获多个异常#
try:
x = int(input("输入一个数字: "))
result = 10 / x
except ValueError:
print("输入的不是数字!")
except ZeroDivisionError:
print("不能除以零!")
# 捕获多个异常类型
try:
# 代码
pass
except (ValueError, TypeError):
print("值错误或类型错误")
# 捕获所有异常(不推荐,除非必要)
try:
# 代码
pass
except Exception as e:
print(f"发生错误: {e}")获取异常信息#
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"错误类型: {type(e).__name__}")
print(f"错误信息: {e}")
print(f"错误详情: {e.args}")try-except-else 语句#
else 子句在没有异常时执行。
try:
x = int(input("输入一个数字: "))
result = 10 / x
except ValueError:
print("输入的不是数字!")
except ZeroDivisionError:
print("不能除以零!")
else:
print(f"结果是: {result}") # 只有在没有异常时执行try-except-finally 语句#
finally 子句无论是否发生异常都会执行。
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("文件不存在!")
finally:
file.close() # 无论是否发生异常都会执行
print("清理工作完成")使用 with 语句(推荐)#
# 使用with语句自动处理资源清理
try:
with open("data.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("文件不存在!")
# 文件会自动关闭,不需要finally常见异常类型#
# ValueError - 值错误
int("abc") # ValueError: invalid literal for int()
# TypeError - 类型错误
"hello" + 5 # TypeError: can only concatenate str (not "int") to str
# IndexError - 索引错误
lst = [1, 2, 3]
lst[10] # IndexError: list index out of range
# KeyError - 键错误
d = {"a": 1}
d["b"] # KeyError: 'b'
# AttributeError - 属性错误
x = 10
x.append(5) # AttributeError: 'int' object has no attribute 'append'
# NameError - 名称错误
print(undefined_var) # NameError: name 'undefined_var' is not defined
# ZeroDivisionError - 除零错误
10 / 0 # ZeroDivisionError: division by zero
# FileNotFoundError - 文件未找到
open("nonexistent.txt") # FileNotFoundError: [Errno 2] No such file or directoryraise 语句#
使用 raise 可以主动抛出异常。
# 抛出异常
x = -1
if x < 0:
raise ValueError("x不能为负数")
# 重新抛出异常
try:
x = 10 / 0
except ZeroDivisionError:
print("捕获到异常")
raise # 重新抛出异常
# 抛出不同类型的异常
try:
x = 10 / 0
except ZeroDivisionError:
raise ValueError("转换错误") # 抛出新的异常类型自定义异常#
Python 中的异常都是类,抛出异常就是创建该异常类的实例
自定义异常类可以更好地表达特定的错误情况,提高代码可读性和错误处理的精确性。
基本定义#
# 简单自定义异常
class MyCustomError(Exception):
"""自定义异常"""
pass
# 带自定义消息的异常
class ValidationError(Exception):
"""验证错误"""
def __init__(self, message, field):
self.message = message
self.field = field
super().__init__(self.message)
# 使用自定义异常
def validate_age(age):
if age < 0:
raise ValidationError("年龄不能为负数", "age")
if age > 150:
raise ValidationError("年龄不合理", "age")
try:
validate_age(-5)
except ValidationError as e:
print(f"验证失败: {e.message}, 字段: {e.field}")异常继承层次#
# 定义异常基类
class APIError(Exception):
"""API错误基类"""
def __init__(self, message, status_code=500):
self.message = message
self.status_code = status_code
super().__init__(self.message)
# 定义具体异常
class NotFoundError(APIError):
"""资源未找到"""
def __init__(self, message="资源未找到"):
super().__init__(message, status_code=404)
class AuthenticationError(APIError):
"""认证错误"""
def __init__(self, message="认证失败"):
super().__init__(message, status_code=401)
# 使用
try:
raise NotFoundError("用户不存在")
except APIError as e:
print(f"API错误: {e.message}, 状态码: {e.status_code}")带额外属性的异常#
class FileProcessingError(Exception):
"""文件处理错误"""
def __init__(self, message, filename, line_number=None):
self.message = message
self.filename = filename
self.line_number = line_number
super().__init__(self.message)
def __str__(self):
"""自定义异常显示"""
base_msg = f"{self.message} (文件: {self.filename}"
if self.line_number:
base_msg += f", 行号: {self.line_number}"
return base_msg + ")"
# 使用
try:
raise FileProcessingError("格式错误", "data.txt", line_number=42)
except FileProcessingError as e:
print(e) # 格式错误 (文件: data.txt, 行号: 42)使用规则#
- 继承 Exception:自定义异常必须继承
Exception或其子类 - 命名规范:异常类名以
Error或Exception结尾 - 文档字符串:使用文档字符串说明异常用途
- 初始化方法:重写
__init__添加自定义属性,调用super().__init__() - 异常消息:通过
super().__init__(message)设置异常消息 - 继承层次:可以创建异常基类和具体异常类,形成异常层次结构
- 自定义显示:重写
__str__方法自定义异常显示格式
异常链#
Python 3 支持异常链,可以保留原始异常信息。
try:
x = 10 / 0
except ZeroDivisionError as e:
raise ValueError("处理错误") from e
# 输出会显示两个异常:ValueError 和 ZeroDivisionError
# 使用 from None 隐藏原始异常
try:
x = 10 / 0
except ZeroDivisionError:
raise ValueError("处理错误") from None
# 只显示 ValueError断言(assert)#
assert 语句用于调试,如果条件为 False,会抛出 AssertionError。
# 基本语法:assert 条件, "错误信息"
x = 5
assert x > 0, "x必须大于0"
x = -1
assert x > 0, "x必须大于0" # AssertionError: x必须大于0
# 注意:在生产环境中,assert可能被禁用(使用 -O 选项)
# 不要用assert来处理应该用异常处理的错误异常处理最佳实践#
1. 具体化异常类型#
# 不好的做法
try:
# 代码
pass
except:
pass # 捕获所有异常,隐藏了问题
# 好的做法
try:
# 代码
pass
except SpecificError as e:
# 处理特定异常
pass2. 不要过度使用异常#
# 不好的做法:用异常处理正常流程
try:
value = my_dict["key"]
except KeyError:
value = None
# 好的做法:先检查
value = my_dict.get("key", None)3. 清理资源#
# 使用with语句或finally确保资源被清理
try:
file = open("data.txt")
# 处理文件
finally:
file.close()
# 或使用with语句(推荐)
with open("data.txt") as file:
# 处理文件
pass4. 提供有意义的错误信息#
# 不好的做法
raise ValueError("错误")
# 好的做法
raise ValueError(f"年龄 {age} 不在有效范围内(0-150)")5. 记录异常#
import logging
try:
# 代码
pass
except Exception as e:
logging.error(f"发生错误: {e}", exc_info=True)
raise # 重新抛出异常上下文管理器#
上下文管理器用于确保资源被正确获取和释放。
# 自定义上下文管理器
class MyContextManager:
def __enter__(self):
print("进入上下文")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出上下文")
return False # 返回False表示不抑制异常
# 使用
with MyContextManager() as cm:
print("在上下文中")
# 输出:
# 进入上下文
# 在上下文中
# 退出上下文实际应用示例#
def safe_divide(a, b):
"""安全除法"""
try:
result = a / b
except ZeroDivisionError:
print("错误:除数不能为零")
return None
except TypeError:
print("错误:参数类型不正确")
return None
else:
return result
# 文件处理
def read_file(filename):
"""读取文件"""
try:
with open(filename, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print(f"错误:文件 {filename} 不存在")
return None
except PermissionError:
print(f"错误:没有权限读取文件 {filename}")
return None
except Exception as e:
print(f"未知错误: {e}")
return None下一步#
掌握了异常处理后,可以学习: