Python 第九课:错误与异常详解:从基础到自定义异常(含完整代码示例)
本文系统介绍了Python中的错误(Error)与异常(Exception)处理。错误指语法问题,必须修正代码;异常是运行时问题,可通过try-except捕获处理。文章详细讲解了异常处理机制,包括:捕获指定异常类型、获取异常信息、多异常处理、try-except-else-finally完整结构、手动抛出异常(raise)、异常传递机制以及自定义异常类实现。通过丰富代码示例演示了各种异常处理场景
在 Python 编程中,错误和异常是两个容易混淆但非常重要的概念。正确理解并处理它们,能让你的程序更加健壮、友好。本文将从基础概念讲起,逐步深入异常处理的各种用法,最后带你实现自定义异常,每个知识点都配有定义和代码示例。
一、错误 vs. 异常(定义 + 对比表)
1.1 定义
-
错误(Error):指代码语法本身有错误,解释器无法执行。这类问题无法通过异常处理机制解决,必须手动修正代码。
-
异常(Exception):代码语法正确,但在执行过程中出现了问题(如除零、索引越界)。这类问题可以通过
try...except机制捕获并处理,避免程序崩溃。
1.2 对比表
| 特性 | 错误 | 异常 |
|---|---|---|
| 本质 | 代码语法错误,解释器无法执行 | 语法正确,但运行时出现问题 |
| 能否处理 | 无法通过异常处理机制解决 | 可以通过 try...except 捕获处理 |
| 例子 | 缺少冒号、缩进错误、括号不匹配 | 除零错误、索引越界、键不存在 |
1.3 代码示例
错误示例(语法错误)
# 下面的代码会报 SyntaxError,因为 if 语句后面缺少冒号
age = 18
if age >= 18 # 缺少冒号
print('chengnian')
异常示例(运行时异常)
# AttributeError:对象没有该属性
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person('张三', 18)
print(p1.name) # 正常输出:张三
print(p1.height) # AttributeError: 'Person' object has no attribute 'height'
# IndexError:列表索引超出范围
l1 = [1, 2, 3]
print(l1[5]) # IndexError: list index out of range
# NameError:变量未定义
print(Name) # NameError: name 'Name' is not defined
# KeyError:字典键不存在
s1 = {'name':'zhangsan', 'age':18}
print(s1['height']) # KeyError: 'height'
二、为什么要处理异常?(定义)
定义:当程序运行时出现异常且未被处理时,异常之后的代码将无法执行,程序会直接终止。异常处理的目的是捕获异常,然后根据业务逻辑决定如何处理(如重试、记录日志、给出友好提示),而不是让异常消失。这样可以提高程序的健壮性和用户体验。
2.1 未处理异常的后果(代码示例)
# 没有进行异常处理
print('欢迎使用本程序')
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
print('这是后续的其他的逻辑1') # 如果上面出现异常,这行不会执行
print('这是后续的其他的逻辑2')
运行结果(当用户输入 10 和 0 时):
欢迎使用本程序
请输入第一个数字:10
请输入第二个数字:0
ZeroDivisionError: division by zero
后面的两行 print 永远不会执行。
三、异常处理基础:try...except(定义 + 示例)
3.1 基本语法定义
定义:
-
try块:存放可能出现问题的代码。 -
except块:当try中的代码出现异常时,程序自动跳转到except块执行。 -
如果
try中没有异常,except块会被跳过。 -
无论是否发生异常,
try...except结构后面的代码都会继续执行(除非程序被强制退出)。
代码示例:
print('欢迎使用本程序')
try:
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
except:
print('程序出现异常!!!')
print('这是后续的其他的逻辑1')
print('这是后续的其他的逻辑2')
3.2 捕获指定类型的异常(定义)
定义:except 后面可以指定具体的异常类型(如 ZeroDivisionError、ValueError)。这样只有当 try 中抛出指定类型的异常时,才会进入该 except 块。这比捕获所有异常更精确、更安全。
代码示例:
print('欢迎使用本程序')
try:
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
except ZeroDivisionError:
print('除数不能为零')
except ValueError:
print('请输入有效的整数')
3.3 验证异常之间的继承关系(定义 + 示例)
定义:Python 中所有异常都继承自 BaseException,而大多数常规异常继承自 Exception。了解继承关系有助于我们安排 except 的顺序:子类异常应写在父类异常前面,否则父类会提前捕获,导致子类异常无法被精确处理。
代码示例:
# 验证异常之间的继承关系
print(ZeroDivisionError, ArithmeticError)
print(issubclass(ZeroDivisionError, ArithmeticError)) # True
print(issubclass(ZeroDivisionError, Exception)) # True
print(issubclass(ZeroDivisionError, BaseException)) # True
print(issubclass(ValueError, BaseException)) # True
print(issubclass(KeyboardInterrupt, Exception)) # False (KeyboardInterrupt 继承自 BaseException)
3.4 多个 except 的匹配顺序(定义 + 示例)
定义:当有多个 except 块时,Python 会从上往下依次匹配异常类型,一旦匹配成功,就执行对应的 except 块,并且不再继续向下匹配。因此,应该将更具体的异常(子类)放在前面,更通用的异常(父类)放在后面。
代码示例:
print('欢迎使用本程序')
try:
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
except ZeroDivisionError:
print('程序出现了异常1(除零错误)')
except ValueError:
print('程序出现了异常2(值错误)')
except Exception:
print('程序出现其他异常!!') # 这个会捕获所有未在前面匹配到的异常
print('后续逻辑继续执行')
3.5 获取异常信息(定义 + 示例)
定义:使用 as e 可以将捕获到的异常对象赋值给变量 e,通过 e 可以获取异常的类型、详细信息等,便于日志记录或更精细的处理。
代码示例:
print('欢迎使用本程序')
try:
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
except ZeroDivisionError:
print('程序出现了异常1')
except ValueError:
print('程序出现了异常2')
except Exception as e:
print('程序出现异常!!')
print(f'异常类型:{type(e)}')
print(f'异常信息:{e}')
print('后续逻辑继续执行')
3.6 一个 except 捕获多个异常(定义 + 示例)
定义:可以使用元组 (异常类型1, 异常类型2, ...) 在一个 except 块中捕获多种异常。然后在块内部可以通过 isinstance() 判断具体是哪种异常,分别处理。
代码示例:
print('欢迎使用本程序')
try:
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
except (ZeroDivisionError, ValueError) as e:
print('程序出现了异常')
print(f'异常类型:{type(e)}')
print(f'异常信息:{e}')
if isinstance(e, ZeroDivisionError):
print('0不能作为除数')
elif isinstance(e, ValueError):
print('你必须输入整数')
四、完整异常处理结构:try-except-else-finally(定义 + 示例)
定义:
-
else:只有在try块没有发生任何异常时才会执行。 -
finally:无论是否发生异常,都会在最后执行。通常用于释放资源(如关闭文件、断开数据库连接)。
代码示例:
print('欢迎使用本程序')
try:
a = int(input('请输入第一个数字:'))
b = int(input('请输入第二个数字:'))
res = a / b
print(f'{a}除以{b}的结果为:{a/b}')
except (ZeroDivisionError, ValueError) as e:
print('程序出现了异常')
print(f'异常信息:{e}')
else:
print('代码没有异常,计算成功') # 只有没有异常时才会执行
finally:
print('无论是否有异常,我都会执行') # 总是执行
print('后续逻辑继续执行')
五、手动抛出异常:raise(定义 + 示例)
定义:当业务逻辑不符合预期时(例如年龄不在合理范围内),可以使用 raise 语句主动抛出异常。raise 后面通常跟一个异常类的实例(如 ValueError('错误信息'))。这样做可以让调用者明确知道发生了业务层面的错误。
代码示例:
print('年龄判断')
try:
age = int(input('请输入你的年龄:'))
if 18 <= age <= 120:
print('你成年了')
elif 0 <= age < 18:
print('未成年')
else:
# 手动抛出异常,自定义错误信息
raise ValueError('年龄应为0-120')
except Exception as e:
print(f'异常:{e}')
六、异常的传递机制(定义 + 示例)
定义:如果当前函数没有捕获异常,那么该异常会沿着调用链逐层向上传递,直到被某个 try-except 捕获。如果所有调用者都没有处理,最终程序会因为未处理异常而终止。这种机制称为异常的传递(或冒泡)。
代码示例:
def func1():
print("func1 开始")
return 1 / 0 # 产生 ZeroDivisionError
print("func1 结束") # 不会执行
def func2():
print("func2 开始")
func1() # 没有处理异常,异常向上传递
print("func2 结束") # 不会执行
def func3():
print("func3 开始")
try:
func2()
except ZeroDivisionError as e:
print(f"在 func3 中捕获到异常:{e}")
print("func3 结束")
# 调用 func3
func3()
输出:
func3 开始
func2 开始
func1 开始
在 func3 中捕获到异常:division by zero
func3 结束
七、自定义异常(定义 + 示例)
定义:当内置异常无法准确表达业务需求时,可以自定义异常类。规则如下:
-
类名通常以
Error结尾(推荐)。 -
必须继承自
Exception或其子类。 -
可以重写
__init__方法,添加自定义的错误信息或属性。
代码示例:
# 自定义异常类
class SchoolNameError(Exception):
def __init__(self, msg):
# 调用父类的初始化方法,传入错误信息
super().__init__('校验异常:' + msg)
def check_name(name):
if len(name) >= 4:
raise SchoolNameError('学校名字过长')
else:
print('名字没问题')
try:
check_name('五六七八大学')
except SchoolNameError as e:
print(f'程序异常:{e}')
输出:
程序异常:校验异常:学校名字过长
八、总结(表格)
| 知识点 | 核心定义 | 关键字/结构 |
|---|---|---|
| 错误(Error) | 语法错误,无法运行 | 无处理机制 |
| 异常(Exception) | 语法正确,运行时出错 | try...except |
| 捕获指定异常 | 只处理特定类型的异常 | except ZeroDivisionError: |
| 获取异常信息 | 访问异常对象的详细信息 | except ... as e: |
| 一个except多异常 | 用元组同时捕获多种异常 | except (Type1, Type2): |
else |
无异常时执行 | else: |
finally |
无论是否异常都执行(释放资源) | finally: |
| 手动抛出异常 | 主动触发异常 | raise |
| 异常传递 | 异常沿调用链向上冒泡 | 自动发生 |
| 自定义异常 | 继承 Exception,满足业务需求 |
class MyError(Exception): |
掌握异常处理,是写出稳定、可维护 Python 代码的关键一步。希望这篇文章能帮你彻底搞懂 Python 中的错误与异常!
📌 文中所有代码示例均已测试通过(Python 3.x),你可以直接复制运行体验效果。
更多推荐

所有评论(0)