07 错误回收机制
错误处理是编程中不可或缺的部分,能够帮助程序在出现异常时优雅地处理问题,而不是直接崩溃。Python 提供了 try 和 except 语句来实现这一功能。
基本语法结构
try 块包含可能引发异常的代码,except 块则用于捕获并处理这些异常。基础语法如下:
| try:
# 可能引发异常的代码,常见于程序不可控的部分,比如用户输入等
risky_operation()
except ExceptionType:
# 异常处理代码
handle_error()
|
基础异常捕获
捕获特定异常
可以指定捕获特定类型的异常,以便针对不同错误采取不同的处理措施。
| try:
result = 10 / 0 # 这里会出现除0错误
except ZeroDivisionError:
print("不能除以零")
|
捕获多个异常
通过多个 except 块可以分别处理不同类型的异常。
| try:
# 可能引发异常的代码
file = open("nonexistent_file.txt", "r")
data = file.read()
except FileNotFoundError:
print("文件未找到")
except PermissionError:
print("没有文件访问权限")
|
捕获所有异常
使用 except 而不指定异常类型可以捕获所有异常,但不推荐在生产代码中滥用,因为可能掩盖潜在问题(报错的时候只知道出现了错误,但是具体原因不清楚,如果这样的潜在问题越来越多,可能影响到整体系统的稳定性,有些错误是可以通过人为修正的)。
| try:
risky_operation()
except:
print("发生了一个错误")
|
使用 else 和 finally
else 块在 try 块未引发任何异常时执行,finally 块无论是否发生异常都会执行,通常用于资源清理。
| try:
result = 10 / 2
except ZeroDivisionError:
print("除以零错误")
else:
print("结果是", result)
finally:
print("执行结束")
|
自定义异常信息
可以通过 as 关键字获取异常的详细信息,便于调试或记录。
| try: # 模拟一个除零错误
a = 10
b = 0
result = a / b
print("计算结果:", result)
except Exception as e: # 这将会打印具体的错误信息
print(f"发生错误: {e}") # 输出:发生错误: division by zero
|
重新抛出异常
在 except 块中可以使用 raise 重新抛出当前异常,便于上层代码进一步处理,我们看以下几种情况,深入了解一下什么叫做提交到上层代码。
1、有都有raise的情况
| def divide(a, b):
if b == 0:
raise ValueError("除数不能为 0")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print("捕获到异常,但重新抛出")
raise
|
在这个情况中,try 中调用了函数 divide ,因此可以简单理解为 try 的入口就是 divide 的上层,因此发生除0错误的时候,先将错误提交到 try,此时try发现这个是个错误,就会交给 except 处理,因此先输出 捕获到异常,但重新抛出,但是接着又跟了一个 raise ,而此处已经没有上层了,所以只能显示错误。
| 捕获到异常,但重新抛出
Traceback (most recent call last):
File "d:\pythonProject\BaseLessons\jr_001_test.py", line 7, in <module>
result = divide(10, 0)
^^^^^^^^^^^^^
File "d:\pythonProject\BaseLessons\jr_001_test.py", line 3, in divide
raise ValueError("除数不能为 0")
ValueError: 除数不能为 0
|
2、函数中不写raise
| def divide(a, b):
# if b == 0:
# raise ValueError("除数不能为 0")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print("捕获到异常,但重新抛出")
raise
|
当我们修改一下,由于 divide 中没有用 raise 向上层返回,也没有在内部使用 try ... except 的错误回收机制,因此直接抛出了错误,程序终止,都没有执行主模块中 except 的部分。
| Traceback (most recent call last):
File "d:\pythonProject\BaseLessons\jr_001_test.py", line 7, in <module>
result = divide(10, 0)
^^^^^^^^^^^^^
File "d:\pythonProject\BaseLessons\jr_001_test.py", line 4, in divide
return a / b
~~^~~
ZeroDivisionError: division by zero
|
3、except中不写 raise
| def divide(a, b):
if b == 0:
raise ValueError("除数不能为 0")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print("捕获到异常,但重新抛出")
# raise
|
这个时候由于except中没有再向上层 raise 错误,在逻辑上是正确的,因此直接输出正确的错误处理逻辑,所以在终端只能看到规范后的提示信息,而不会直接报错。
实际应用示例
以下是一个读取文件内容的完整示例,展示了 try 和 except 的实际应用场景。
| def read_file(filename):
try:
with open(filename, "r") as file:
content = file.read()
return content
except FileNotFoundError:
return "文件未找到"
except PermissionError:
return "没有权限读取文件"
except Exception as e:
return f"发生未知错误: {e}"
content = read_file("example.txt")
print(content)
|
避免捕获过于宽泛的异常,应尽量精确处理已知的异常类型。finally 块用于释放资源(如关闭文件或数据库连接)。异常处理应当注重可读性和可维护性,复杂的错误处理逻辑可以封装为单独的函数。