1. 异常的概念
在Python中,异常(Exception)是程序运行时出现的错误或不正常情况。异常通常表示程序在运行时遇到了无法继续执行的条件。Python通过 try
/except
语句来捕获和处理异常。
异常可以分为两类:
- 内建异常:Python提供了一些内建的异常类,表示常见的错误情况,比如
IndexError
、TypeError
、ValueError
等。 - 自定义异常:开发者可以自定义异常类型来表示特定的错误或问题。
2. 常见的内建异常
Python中有很多内建异常类,每种异常类表示不同的错误类型。以下是一些常见的内建异常:
- SyntaxError:语法错误,表示代码语法不正确。
- TypeError:类型错误,表示操作或函数应用于错误类型的对象。
- ValueError:值错误,表示传递给函数的参数的值不合适。
- IndexError:索引错误,表示访问的索引超出了范围。
- KeyError:键错误,表示在字典中访问一个不存在的键。
- AttributeError:属性错误,表示访问对象不存在的属性。
- ZeroDivisionError:除零错误,表示除法操作中的除数为零。
- FileNotFoundError:文件未找到错误,表示操作文件时找不到指定的文件。
- Exception:所有异常的基类,捕获其它未处理的异常时,通常使用
Exception
。
3. 异常处理的基本语法
Python使用 try
和 except
块来捕获和处理异常:
try:
# 可能会引发异常的代码
result = 10 / 0
except ZeroDivisionError as e:
# 处理异常的代码
print(f"错误: {e}")
try
:用于包裹可能引发异常的代码块。except
:用于捕获和处理异常。如果try
代码块中的某些代码引发了异常,程序会跳转到相应的except
块执行。as
:用于给捕获到的异常实例命名,可以在except
块中使用该实例进行进一步处理。
4. 多个异常的处理
你可以在同一个 try
块中捕获多种不同类型的异常:
try:
x = int(input("请输入一个数字: "))
result = 10 / x
except ValueError:
print("输入的不是有效的数字")
except ZeroDivisionError:
print("不能除以零")
except Exception as e:
print(f"发生了其他错误: {e}")
- 在
try
代码块中,如果出现多个异常,它们会按照except
块的顺序进行匹配。 - 如果异常不在匹配的范围内,会触发
Exception
类捕获,Exception可以捕获所有异常,因为所有的异常都是Exception的子类。
5. else
和 finally
Python的异常处理语句还支持 else
和 finally
代码块:
else
:如果try
代码块没有引发任何异常,else
代码块会被执行。finally
:无论是否发生异常,finally
代码块中的内容都会被执行,通常用于清理资源,如关闭文件或数据库连接。
try:
result = 10 / 2
except ZeroDivisionError:
print("不能除以零")
else:
print("计算成功,结果是:", result)
finally:
print("清理操作,始终执行")
else
语句块中的代码只有在try
代码块没有引发异常时才会执行。finally
语句块中的代码无论如何都会执行,适合进行资源的释放、日志记录等操作。
6. 抛出异常(raise
)
除捕获异常,Python还允许你显式地抛出异常。你可以使用 raise
语句来抛出异常,并且可以选择抛出特定类型的异常:了
def check_age(age):
if age < 0:
raise ValueError("年龄不能为负")
return age
try:
check_age(-5)
except ValueError as e:
print(e)
raise
语句用于抛出异常。你可以在抛出异常时传递一个错误消息(如ValueError("年龄不能为负")
),并且可以自定义错误类型。在except语句中可以直接使用raise重新抛出该异常,让上一层函数处理。def function(value): try: return int(value) # 尝试将值转换为整数 except ValueError as e: # 如果发生 ValueError 异常,执行此处 print(f"function_c: 捕获到 ValueError: {e}") raise # 重新抛出异常,以便向上传递
- 你还可以抛出现有的异常类,也可以自定义异常类。
7. 自定义异常
你可以创建自己的异常类,继承自 Exception
类,来表示特定的错误类型。通常情况下,异常类会有一个 __init__
方法来接收错误信息或其他有用的参数。
class MyCustomError(Exception):
"""自定义异常类"""
def __init__(self, message, code):
super().__init__(message)
self.code = code
def my_function(value):
if value < 0:
raise MyCustomError("值不能为负数!", 1001)
try:
my_function(-10)
except MyCustomError as e:
print(f"捕获到自定义异常:{e}, 错误代码:{e.code}")
8. ExceptionGroup
(Python 3.11 及之后)
Python 3.11 引入了 ExceptionGroup
,这是一个新的异常类型,允许你将多个异常组合在一起。在处理并发任务或多任务环境时,多个异常可能同时发生,此时可以将这些异常组织到ExceptionGroup
中,便于统一处理。
def f():
raise ExceptionGroup(
"Multiple errors occurred",
[ValueError("Invalid value"), KeyError("Missing key")],
)
try:
f()
except ExceptionGroup as e:
print(f"捕获到异常组: {e}")
9. 异常的传递
异常的传播遵循以下过程:
- 异常抛出:当程序执行过程中发生错误时,Python 会抛出一个异常。例如,试图将一个字符串转换为整数时,Python 会抛出
ValueError
。 - 异常传播:抛出的异常会沿着调用栈(即函数调用的路径)传播,寻找合适的
except
块来捕获它。如果当前函数没有捕获该异常,异常会继续向上传递到调用该函数的地方。 - 捕获异常:一旦异常找到合适的
except
块,程序就会跳转到这个except
块,执行其中的处理代码。异常传递过程会停止,程序还能正常继续运行剩余内容。 - 未捕获异常:如果异常没有在任何
except
块中被捕获,它会继续向上传递,直到程序的顶层。如果在顶层没有合适的异常处理代码,程序会终止并显示异常信息。
例子:
--- 测试用例 1:正常情况 ---
function_b: function_c 返回结果: 123
function_a: function_b 返回结果: 246
--- 测试用例 2:function_c 抛出 ValueError,function_b 捕获并处理 ---
function_c: 捕获到 ValueError: invalid literal for int() with base 10: 'abc'
function_b: 捕获到 ValueError: invalid literal for int() with base 10: 'abc',进行特殊处理
function_a: function_b 返回结果: -1
--- 测试用例 3:function_c 抛出 ValueError,function_b 没有捕获,function_a 捕获 ---
function_c: 捕获到 ValueError: invalid literal for int() with base 10: 'xyz'
function_a_catch_all: 捕获到其他异常:ValueError invalid literal for int() with base 10: 'xyz'
--- 测试用例 4:function_c 抛出 ValueError,所有函数都没有捕获 ---
function_c: 捕获到 ValueError: invalid literal for int() with base 10: 'error'
Traceback (most recent call last):
File "g:\python_tutorial-master\progarm\ex.py", line 63, in <module>
Traceback (most recent call last):
File "g:\python_tutorial-master\progarm\ex.py", line 63, in <module>
File "g:\python_tutorial-master\progarm\ex.py", line 63, in <module>
function_a_no_catch("error") # ValueError 没有被捕获,程序终止
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
function_a_no_catch("error") # ValueError 没有被捕获,程序终止
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 60, in function_a_no_catch
final_result = function_b_no_catch_2(item)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 57, in function_b_no_catch_2
return function_c(data)
^^^^^^^^^^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 7, in function_c
raise e # 重新抛出异常,让上一层函数处理
^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 4, in function_c
return int(value)
^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'error'
测试用例1:正常情况
--- 测试用例 1:正常情况 ---
function_b: function_c 返回结果: 123
function_a: function_b 返回结果: 246
function_c
成功将 "123" 转换为整数,结果逐层返回,没有异常发生。
测试用例 2:function_c 抛出 ValueError,function_b 捕获并处理
--- 测试用例 2:function_c 抛出 ValueError,function_b 捕获并处理 ---
function_c: 捕获到 ValueError: invalid literal for int() with base 10: 'abc'
function_b: 捕获到 ValueError: invalid literal for int() with base 10: 'abc',进行特殊处理
function_a: function_b 返回结果: -1
function_c
无法将 "abc" 转换为整数,抛出 ValueError
。function_b
捕获了这个异常,进行了特殊处理(返回 -1),异常传播停止。
测试用例 3:function_c 抛出 ValueError,function_b 没有捕获,function_a 捕获
--- 测试用例 3:function_c 抛出 ValueError,function_b 没有捕获,function_a 捕获 ---
function_c: 捕获到 ValueError: invalid literal for int() with base 10: 'xyz'
function_a_catch_all: 捕获到其他异常:ValueError invalid literal for int() with base 10: 'xyz'
function_c
抛出 ValueError
,function_b_no_catch
没有捕获,异常继续向上传播到 function_a_catch_all
,由于function_a_catch_all
使用了except Exception
捕获了所有类型的异常,所以异常在这里被处理了。
测试用例 4:function_c 抛出 ValueError,所有函数都没有捕获
--- 测试用例 4:function_c 抛出 ValueError,所有函数都没有捕获 ---
function_c: 捕获到 ValueError: invalid literal for int() with base 10: 'error'
Traceback (most recent call last):
File "g:\python_tutorial-master\progarm\ex.py", line 63, in <module>
Traceback (most recent call last):
File "g:\python_tutorial-master\progarm\ex.py", line 63, in <module>
File "g:\python_tutorial-master\progarm\ex.py", line 63, in <module>
function_a_no_catch("error") # ValueError 没有被捕获,程序终止
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
function_a_no_catch("error") # ValueError 没有被捕获,程序终止
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 60, in function_a_no_catch
final_result = function_b_no_catch_2(item)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 57, in function_b_no_catch_2
return function_c(data)
^^^^^^^^^^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 7, in function_c
raise e # 重新抛出异常,让上一层函数处理
^^^^^^^
File "g:\python_tutorial-master\progarm\ex.py", line 4, in function_c
return int(value)
^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'error'
function_c
抛出 ValueError
,所有函数都没有捕获,异常最终传播到程序顶层,导致程序终止并打印错误信息。