6.Python基础:异常
Python异常处理机制能够优雅地应对程序运行时的错误。异常分为不同类型(如ZeroDivisionError、FileNotFoundError等),继承自BaseException类。通过try-except语句可捕获和处理异常,其中try块监控可能出错的代码,except块处理特定异常。扩展语法包括else(无异常时执行)和finally(必须执行的清理代码)。此外,使用raise和asse
6.Python基础:异常
在 Python 程序开发和运行过程中,“异常” 是不可避免的 —— 它可能源于代码逻辑错误(如除以 0)、外部环境变化(如文件不存在)或用户操作不当(如输入非数字)。若不处理异常,程序会直接崩溃并抛出错误信息;通过合理的异常处理,可让程序优雅地应对错误,保障程序稳定性。
6.1 异常概述
异常(Exception)是程序运行时发生的 “意外错误”,Python 会将异常封装为一个 “异常对象”,包含错误位置、类型和描述信息。若程序未处理异常,Python 解释器会采用默认处理方式:打印异常信息(Traceback)、终止程序运行。
6.1.1 异常示例
以下代码因 “除以 0” 触发ZeroDivisionError异常,程序崩溃并输出异常信息:
print(5 / 0)
运行结果(异常信息):
Traceback (most recent call last):
File "E:\pyproject\异常\异常示例.py", line 1, in <module>
print(5 / 0)
ZeroDivisionError: division by zero
异常信息包含三部分核心内容:
- Traceback:错误堆栈,显示异常发生的代码路径(从外层到内层);
- 错误行号:明确异常发生在哪个文件的哪一行(如
line 1); - 异常类型与描述:
ZeroDivisionError是异常类型,division by zero是异常描述(说明错误原因)。
6.1.2 异常类型
Python 中的所有异常都继承自BaseException类,常用的异常类型及触发场景如下:
| 异常类型 | 触发场景 | 示例 |
|---|---|---|
NameError |
使用未定义的变量 | print(test)(test 未声明) |
IndexError |
列表 / 元组等序列越界访问 | num_list = []; num_list[0] |
AttributeError |
访问对象不存在的属性或方法 | class Car: pass; car = Car(); print(car.color) |
FileNotFoundError |
打开不存在的文件或目录 | open("test.txt")(test.txt 不存在) |
ZeroDivisionError |
除法运算中除数为 0 | 5 / 0 |
ValueError |
数据类型正确但值不符合要求 | int("abc")(字符串无法转为整数) |
TypeError |
操作或函数应用于错误类型的对象 | 1 + "2"(整数与字符串无法相加) |
6.1.3 异常的层级关系
Python 异常体系的核心层级如下:
- BaseException:所有异常的基类;
KeyboardInterrupt:用户按下Ctrl+C中断程序;SystemExit:Python 解释器退出;
- Exception:所有 “非退出类” 异常的基类(开发中主要处理此类异常);
ZeroDivisionError、IndexError、FileNotFoundError等:具体异常类型。
开发中通常捕获Exception类(或其子类),避免捕获BaseException(防止捕获到SystemExit等退出异常,导致程序无法正常退出)。
6.2 异常捕获语句
Python 提供try-except语句捕获并处理异常,可根据需求组合else和finally子句,实现更灵活的异常处理逻辑。
6.2.1 基础语法:try-except
try-except是最核心的异常捕获结构,用于 “监控可能出错的代码”,并在异常发生时执行处理逻辑。
语法格式
try:
# 可能触发异常的代码块(监控区域)
代码1
代码2
except [异常类型1 [as 变量名1]]:
# 捕获到“异常类型1”时执行的处理逻辑
处理代码1
except [异常类型2 [as 变量名2]]:
# 捕获到“异常类型2”时执行的处理逻辑
处理代码2
...
关键说明
try块:必须有,用于包裹 “可能出错的代码”,若代码无异常,except块会被跳过;except块:可多个,用于捕获指定类型的异常,[异常类型]省略时捕获所有Exception类异常;as 变量名:可选,将捕获到的异常对象赋值给变量,可通过变量获取异常详情(如print(变量名)打印异常描述)。
示例 1:捕获单个异常
# 监控“除法运算”,捕获ZeroDivisionError
num1 = int(input("请输入被除数:"))
num2 = int(input("请输入除数:"))
try:
result = num1 / num2
print(f"结果:{result}")
except ZeroDivisionError as e:
# 处理“除数为0”的异常
print(f"出错了:{e}") # 输出:出错了:division by zero
示例 2:捕获多个异常
# 监控“输入+除法”,捕获ValueError和ZeroDivisionError
try:
num1 = int(input("请输入被除数:")) # 可能触发ValueError(输入非数字)
num2 = int(input("请输入除数:")) # 可能触发ValueError
result = num1 / num2 # 可能触发ZeroDivisionError
print(f"结果:{result}")
except ValueError as e:
print(f"输入错误:{e}") # 处理“输入非数字”
except ZeroDivisionError as e:
print(f"计算错误:{e}") # 处理“除数为0”
示例 3:捕获所有异常(不推荐)
# 捕获所有Exception类异常(适合临时调试,不推荐正式环境使用)
try:
num1 = int(input("请输入被除数:"))
num2 = int(input("请输入除数:"))
result = num1 / num2
print(f"结果:{result}")
except Exception as e:
print(f"程序出错:{e}") # 所有异常都会进入这里处理
6.2.2 扩展语法 1:try-except-else
else子句用于定义 “try块无异常时执行的逻辑”—— 仅当try块中的代码完全无异常时,else块才会执行;若try块触发异常,else块会被跳过。
语法格式
try:
可能出错的代码
except [异常类型] as e:
异常处理代码
else:
无异常时执行的代码
示例
try:
num1 = int(input("请输入被除数:"))
num2 = int(input("请输入除数:"))
result = num1 / num2
except (ValueError, ZeroDivisionError) as e:
print(f"出错了:{e}")
else:
# 仅当try块无异常时执行
print(f"计算成功!结果:{result}")
6.2.3 扩展语法 2:try-except-finally
finally子句用于定义 “无论try块是否有异常,都必须执行的逻辑”—— 通常用于 “资源清理”(如关闭文件、关闭网络连接),确保资源不会因异常而泄漏。
语法格式
try:
可能出错的代码
except [异常类型] as e:
异常处理代码
finally:
必须执行的代码(无论是否有异常)
示例:关闭文件资源
try:
# 尝试打开并读取文件
file = open('f:\\luckycloud.txt', 'r', encoding='utf-8')
print(file.read())
except FileNotFoundError as e:
print(f"文件操作出错:{e}")
finally:
# 无论是否有异常,都关闭文件
if 'file' in locals(): # 判断file变量是否存在(避免未打开文件时调用close())
file.close()
print("文件已关闭")
6.2.4 组合语法:try-except-else-finally
四个子句可组合使用,执行顺序为:
- 执行
try块; - 若
try块有异常:执行对应except块 → 执行finally块; - 若
try块无异常:执行else块 → 执行finally块。
示例
try:
num1 = int(input("请输入被除数:"))
num2 = int(input("请输入除数:"))
result = num1 / num2
except (ValueError, ZeroDivisionError) as e:
print(f"出错了:{e}")
else:
print(f"计算成功!结果:{result}")
finally:
print("程序执行完毕(无论是否有异常,我都会执行)")
6.3 抛出异常
除了 “程序自动触发异常”,Python 还支持通过raise和assert语句 “主动抛出异常”,用于在自定义场景下(如数据校验失败)触发错误,控制程序流程。
6.3.1 raise语句:显式抛出异常
raise语句用于主动抛出指定类型的异常,可自定义异常描述信息,是开发中最常用的 “主动抛异常” 方式。
语法格式
raise语句有三种常用格式:
raise 异常类:抛出指定类型的异常(无描述信息);raise 异常类对象:抛出指定类型的异常(可自定义描述信息);raise:在except块中重新抛出 “刚捕获的异常”(用于向上传递异常)。
示例 1:抛出无描述的异常
# 抛出IndexError异常
raise IndexError
运行结果:
Traceback (most recent call last):
File "E:\pyproject\异常\raise示例1.py", line 2, in <module>
raise IndexError
IndexError
示例 2:抛出带描述的异常
# 抛出带描述信息的IndexError
index_error = IndexError("索引下标超出列表范围")
raise index_error
运行结果:
Traceback (most recent call last):
File "E:\pyproject\异常\raise示例2.py", line 3, in <module>
raise index_error
IndexError: 索引下标超出列表范围
示例 3:重新抛出异常
在except块中捕获异常后,可通过raise重新抛出该异常,让上层代码处理(用于 “异常传递”):
def func():
try:
5 / 0 # 触发ZeroDivisionError
except ZeroDivisionError as e:
print(f"函数内部捕获异常:{e}")
raise # 重新抛出异常,让调用者处理
# 调用func(),处理重新抛出的异常
try:
func()
except ZeroDivisionError as e:
print(f"调用者捕获异常:{e}")
运行结果:
函数内部捕获异常:division by zero
调用者捕获异常:division by zero
示例 4:异常链(raise-from)
通过raise 新异常 from 原异常,可建立 “异常链”—— 新异常由原异常触发,便于追踪异常根源:
try:
5 / 0 # 原异常:ZeroDivisionError
except Exception as original_e:
# 抛出新异常,并关联原异常
raise IndexError("下标超出范围") from original_e
运行结果(包含异常链信息):
Traceback (most recent call last):
File "E:\pyproject\异常\raise_from示例.py", line 2, in <module>
5 / 0
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "E:\pyproject\异常\raise_from示例.py", line 5, in <module>
raise IndexError("下标超出范围") from original_e
IndexError: 下标超出范围
6.3.2 assert语句:断言抛出异常
assert语句(断言)用于 “验证某个条件是否成立”—— 若条件成立,程序继续执行;若条件不成立,抛出AssertionError异常。assert通常用于 “调试阶段的逻辑校验”(如验证函数参数是否合法),不推荐在正式环境依赖assert处理业务逻辑(因为 Python 可通过-O参数关闭断言)。
语法格式
assert 条件表达式[, 异常描述信息]
示例:验证除数不为 0
# 断言“除数不为0”,条件不成立则抛出AssertionError
num1 = int(input("请输入被除数:"))
num2 = int(input("请输入除数:"))
assert num2 != 0, "除数不能为0" # 条件num2 != 0不成立时,抛出异常
result = num1 / num2
print(f"结果:{result}")
运行结果(若输入除数为 0):
请输入被除数:10
请输入除数:0
Traceback (most recent call last):
File "E:\pyproject\异常\assert示例.py", line 4, in <module>
assert num2 != 0, "除数不能为0"
AssertionError: 除数不能为0
6.3.3 异常的传递
异常会沿着 “函数调用栈” 向上传递 —— 若函数 A 调用函数 B,函数 B 中触发异常且未处理,异常会传递到函数 A;若函数 A 也未处理,异常会继续向上传递,直到被捕获或导致程序崩溃。
示例:异常传递
# 函数B:触发异常且未处理
def func_b():
print("进入func_b")
raise ValueError("func_b中发生错误") # 触发异常
# 函数A:调用func_b,未处理异常
def func_a():
print("进入func_a")
func_b() # 调用func_b,异常会传递到这里
print("离开func_a") # 异常未处理,此句不会执行
# 主程序:调用func_a,处理异常
try:
func_a()
except ValueError as e:
print(f"主程序捕获异常:{e}")
运行结果(异常从 func_b 传递到主程序):
进入func_a
进入func_b
主程序捕获异常:func_b中发生错误
6.4 自定义异常
Python 提供的内置异常(如ZeroDivisionError、IndexError)可满足大部分场景,但在特定业务中(如 “密码长度不足”“账号已锁定”),需自定义异常类,让异常更贴合业务逻辑。
6.4.1 自定义异常的规则
自定义异常需满足以下两点:
- 自定义异常类必须继承
Exception类(或其子类),不能继承BaseException(避免与系统退出异常混淆); - 类名通常以 “
Error” 结尾(如ShortPwdError、AccountLockedError),符合 Python 命名规范,便于识别。
6.4.2 自定义异常的实现
步骤
- 定义异常类,继承
Exception; - (可选)在
__init__方法中添加自定义属性(如错误码、详细信息); - 使用
raise语句抛出自定义异常。
示例:自定义 “密码长度不足” 异常
# 1. 自定义异常类:继承Exception
class ShortPwdError(Exception):
"""自定义异常:密码长度不足"""
def __init__(self, current_length, min_length):
# 自定义属性:当前密码长度、最小要求长度
self.current_length = current_length
self.min_length = min_length
# 自定义异常的字符串表示(可选,便于打印异常信息)
def __str__(self):
return f"ShortPwdError:输入的密码长度为{self.current_length},至少需要{self.min_length}个字符"
# 2. 使用自定义异常
try:
pwd = input("请输入密码:")
if len(pwd) < 3:
# 抛出自定义异常
raise ShortPwdError(len(pwd), 3)
print("密码设置成功!")
except ShortPwdError as e:
# 处理自定义异常
print(e) # 输出:ShortPwdError:输入的密码长度为2,至少需要3个字符
6.5 实训案例
6.5.1 头像格式检测
需求
某网站仅允许用户上传jpg、png、jpeg格式的头像文件,需通过异常处理实现以下功能:
- 接收用户输入的文件路径;
- 检测文件扩展名是否在允许范围内,若不在,抛出
UnsupportedFormatError(自定义异常); - 捕获异常并提示用户 “不支持的文件格式,请上传 jpg/png/jpeg 文件”;
- 若文件路径不存在,捕获
FileNotFoundError并提示用户 “文件不存在,请检查路径”。
实现思路
- 自定义
UnsupportedFormatError异常类,继承Exception; - 编写检测函数:提取文件扩展名,判断是否在允许列表(
['jpg', 'png', 'jpeg'])中,不在则抛出自定义异常; - 使用
try-except捕获 “文件不存在” 和 “格式不支持” 两种异常,分别处理。
参考代码
import os
# 1. 自定义异常:不支持的文件格式
class UnsupportedFormatError(Exception):
def __init__(self, file_format, allowed_formats):
self.file_format = file_format
self.allowed_formats = allowed_formats
def __str__(self):
return f"UnsupportedFormatError:不支持{self.file_format}格式,仅允许{self.allowed_formats}格式"
# 2. 检测头像格式
def check_avatar_format(file_path):
# 提取文件扩展名(转为小写)
_, ext = os.path.splitext(file_path) # ext格式为“.jpg”
file_format = ext.lstrip('.').lower() # 去除“.”,转为小写,如“jpg”
# 允许的格式列表
allowed_formats = ['jpg', 'png', 'jpeg']
if file_format not in allowed_formats:
# 抛出自定义异常
raise UnsupportedFormatError(file_format, allowed_formats)
print(f"文件格式检测通过:{file_format}")
# 3. 主逻辑:接收输入并处理异常
if __name__ == "__main__":
file_path = input("请输入头像文件路径:")
try:
# 检查文件是否存在(os.path.exists返回True/False)
if not os.path.exists(file_path):
raise FileNotFoundError(f"文件不存在:{file_path}")
# 检查文件格式
check_avatar_format(file_path)
print("头像上传成功!")
except FileNotFoundError as e:
print(f"错误:{e}")
except UnsupportedFormatError as e:
print(f"错误:{e}")
6.5.2 海鲜超市数量检测
需求
用户在海鲜超市网购时,需选择海鲜种类和购买数量,需实现以下功能:
- 接收用户输入的 “海鲜名称” 和 “购买数量”;
- 检测数量是否为正整数(≥1):
- 若输入非数字,捕获
ValueError,提示 “数量需为数字”,并默认数量为 1; - 若输入数字但小于 1,捕获
ValueError(主动抛出),提示 “数量需≥1”,并默认数量为 1;
- 若输入非数字,捕获
- 输出最终的 “购买信息”(海鲜名称、数量)。
实现思路
- 编写数量检测函数:尝试将输入转为整数,若转换失败抛
ValueError;若转换后数量 < 1,也抛ValueError; - 使用
try-except捕获ValueError,统一处理 “非数字” 和 “数量不足” 两种错误,设置默认数量为 1; - 输出最终购买信息。
参考代码
# 检测购买数量是否合法
def check_quantity(input_quantity):
try:
# 尝试将输入转为整数
quantity = int(input_quantity)
except ValueError:
# 输入非数字,抛出异常
raise ValueError("数量需为数字")
# 数量小于1,抛出异常
if quantity < 1:
raise ValueError("数量需≥1")
return quantity
# 主逻辑
if __name__ == "__main__":
# 接收用户输入
seafood_name = input("请输入海鲜名称:")
input_quantity = input("请输入购买数量:")
try:
# 检测数量
quantity = check_quantity(input_quantity)
except ValueError as e:
# 处理异常,设置默认数量为1
print(f"输入错误:{e},已默认设置数量为1")
quantity = 1
# 输出购买信息
print(f"\n购买信息:")
print(f"海鲜名称:{seafood_name}")
print(f"购买数量:{quantity}")
6.6 本章小结
本章围绕 Python 异常处理展开,核心内容包括:
- 异常的基本概念:异常是程序运行时的意外错误,Python 将其封装为异常对象,包含错误位置、类型和描述;
- 异常捕获语句:
try-except(捕获指定异常)、try-except-else(无异常时执行)、try-except-finally(无论是否有异常都执行),掌握不同场景下的语法选择; - 主动抛出异常:
raise(显式抛出指定异常,支持异常链)、assert(断言条件,调试阶段用),理解异常传递的规则; - 自定义异常:继承
Exception类,定义贴合业务的异常类型,让异常处理更精准; - 实训案例:通过 “头像格式检测”“海鲜数量检测”,掌握异常处理在实际业务中的应用。
通过本章学习,需理解 “异常处理的核心目标”—— 不是避免异常,而是让程序在异常发生时 “不崩溃、有反馈、可恢复”,保障程序的稳定性和用户体验。在实际开发中,需根据业务场景合理选择异常类型、设计处理逻辑,避免 “捕获所有异常”“忽略异常” 等不良实践。
更多推荐


所有评论(0)