Python 零基础入门系列(十三):异常处理

Python 零基础入门系列(十三):异常处理 ️ Python 零基础入门系列十三异常处理让程序在“暴风雨”中优雅地运行而不是轰然倒塌 系列说明在前一篇中我们学会了如何通过文件操作让数据“永生”。但在真实的开发环境中程序往往会遇到各种意外情况文件被删除了、网络断开了、用户输入了非法字符……本文将教你如何使用**异常处理Exception Handling**机制捕获并处理这些运行时错误让你的程序更加健壮和稳定。 更新时间2026 年 3 月 30 日 本篇你将学到常见异常类型、try-except语句、else和finally子句、主动抛出异常、自定义异常⏱️ 预计阅读时间40-50 分钟 | 实践时间30-40 分钟 前置知识已完成前十二篇特别是文件操作和逻辑判断✍️ 作者书到用时方恨少! 前言程序的“意外”时刻在之前的练习中如果你输入了错误的代码比如用数字除以字符串Python 解释器会直接报错并停止运行终端里会打印出一长串红色的英文信息。在编程术语中这被称为异常Exception。如果不处理这些异常程序就会非正常终止Crash。异常处理的核心思想是“防患于未然”。我们预判某些代码可能会出错于是提前写好“应急预案”。当错误发生时程序不会崩溃而是执行我们准备好的处理逻辑比如提示用户重新输入、记录日志、使用默认值等。1. 异常基础1.1 什么是异常Exception异常是 Python 中的一个对象它代表程序运行过程中发生的错误。当 Python 无法正常处理代码时就会创建一个异常对象。1.2 常见的内置异常类型Python 内置了许多异常类型用来区分不同的错误场景。以下是开发中最常遇到的几种异常类型说明触发场景SyntaxError语法错误代码写错了如缺少冒号、括号不匹配无法被捕获必须修改代码IndentationError缩进错误缩进不规范NameError名称错误使用了未定义的变量名TypeError类型错误操作或函数应用于不适当类型的对象如2 2ValueError值错误对象类型正确但值不合法如int(abc)IndexError索引错误序列下标越界KeyError键错误字典中找不到指定的键FileNotFoundError文件不存在试图打开不存在的文件ZeroDivisionError除零错误除数为零1.3 异常的传播Traceback当你没有处理异常时它会沿着函数调用栈向上传播直到被处理或导致程序终止。示例def divide(a, b): return a / b # 这里可能抛出 ZeroDivisionError def main(): divide(10, 0) # 调用 divide main() # 调用 main如果发生除零错误Python 会打印Traceback (most recent call last): File test.py, line 7, in module main() File test.py, line 5, in main divide(10, 0) File test.py, line 2, in divide return a / b ZeroDivisionError: division by zero这就是堆栈跟踪Traceback它能帮你精准定位错误发生的位置。2. 捕获异常2.1try-except语句这是异常处理的核心语法。基本结构是先尝试执行try块中的代码如果出错就跳转到except块处理。语法try: # 可能出现错误的代码 risky_code() except: # 如果出错执行这里的代码 handle_error()2.2 捕获特定异常永远不要裸奔except:。如果不指定异常类型它会捕获所有错误包括你意想不到的致命错误这会让调试变得非常困难。正确写法try: num int(input(请输入一个数字: )) except ValueError: # 只捕获类型转换错误 print(输入的不是有效数字请重新输入)2.3 捕获多个异常如果一段代码可能抛出多种不同的异常你可以使用多个except块。try: file open(data.txt, r) content file.read() number int(content) result 10 / number except FileNotFoundError: print(错误找不到文件 data.txt) except ValueError: print(错误文件内容不是有效的数字) except ZeroDivisionError: print(错误不能除以零)2.4 捕获异常对象as有时候你不仅想知道出错了还想获取具体的错误信息比如错误原因。可以使用as关键字。try: result 10 / 0 except ZeroDivisionError as e: print(f程序出错了{e}) # 输出程序出错了division by zero2.5 万能捕获慎用虽然可以使用Exception来捕获几乎所有的常规错误但通常建议将其放在except块的最后作为兜底方案。try: # 一些复杂的操作 pass except FileNotFoundError as e: print(f文件错误: {e}) except Exception as e: # 捕获其他所有未预料到的错误 print(f未知错误: {e}) # 通常在这里记录日志方便后续排查3. 完整的异常处理结构一个完整的try语句块可以包含else和finally子句。3.1else子句else块在try块没有发生任何异常时执行。它通常用于放置那些只有在没有错误时才应该运行的代码以避免意外捕获这些代码产生的错误。对比# 方式 A没有 else try: num int(input(输入数字: )) print(f你输入了: {num}) # 如果 print 报错也会被下面的 except 捕获虽然概率极低 except ValueError: print(输入错误)# 方式 B推荐使用 else try: num int(input(输入数字: )) except ValueError: print(输入错误) else: # 只有转换成功才会执行 print(f你输入了: {num}) # 这里的 print 报错会直接抛出不会被上面的 except 捕获3.2finally子句finally块无论是否发生异常都会在最后执行。它通常用于释放关键资源例如关闭文件、关闭数据库连接、释放锁等。语法try: file open(test.txt, w) file.write(Hello) # 假设这里发生了一个未被捕获的致命错误 raise RuntimeError(模拟崩溃) except ValueError: print(处理值错误) finally: # 即使上面 raise 了 RuntimeError这里依然会执行 file.close() print(文件已关闭)执行顺序总结执行try块。如果有异常寻找匹配的except块并执行。如果没有异常执行else块。无论前面发生了什么即使程序要崩溃了最后都必须执行finally块。4. 主动抛出异常除了 Python 自动抛出的异常我们也可以在代码中主动抛出异常。4.1raise语句当你检测到某种条件不满足无法继续执行时可以使用raise抛出异常。示例def withdraw_money(balance, amount): if amount balance: # 主动抛出异常终止函数执行 raise ValueError(余额不足无法取款) balance - amount return balance # 调用 try: withdraw_money(100, 200) except ValueError as e: print(e) # 输出余额不足无法取款4.2 重新抛出异常在except块中处理完日志记录等逻辑后如果你想让上层调用者也知道这个错误可以使用raise不带参数重新抛出当前的异常。try: risky_operation() except Exception as e: print(f记录日志操作失败原因{e}) # 记录完日志后继续向上抛出错误让上层处理 raise5. 自定义异常Python 内置的异常类型虽然很多但有时无法精确表达业务逻辑中的错误。这时我们可以创建自定义异常。方法定义一个继承自Exception类或其子类的新类。场景开发一个在线购物系统需要处理“库存不足”的业务异常。# 自定义异常类 class InsufficientStockError(Exception): 库存不足异常 def __init__(self, product_name, stock, requested): self.product_name product_name self.stock stock self.requested requested # 调用父类构造函数 super().__init__(f商品 {product_name} 库存不足。库存: {stock}, 请求: {requested}) # 模拟下单函数 def place_order(product, quantity): stock 10 # 假设库存只有 10 个 if quantity stock: # 抛出自定义异常 raise InsufficientStockError(product, stock, quantity) print(f订单成功购买 {quantity} 个 {product}) # 调用与处理 try: place_order(iPhone, 15) except InsufficientStockError as e: print(f业务错误{e}) print(f请联系客服。商品{e.product_name})6. 异常处理最佳实践具体优于宽泛尽量捕获具体的异常类型如ValueError而不是裸奔except:。这能防止掩盖真正的 Bug。不要忽略异常即使你写了except也至少要打印日志或记录下来。空的except块会让程序在出错时静默失败极难排查。使用finally释放资源文件、网络连接、锁等资源必须在finally中释放或者使用with语句上下文管理器自动管理。尽早抛出晚点捕获在错误发生的源头尽早抛出异常但在有足够信息处理它的地方才捕获。利用异常传递信息通过自定义异常可以将业务逻辑中的错误清晰地表达出来。7. 综合实战带异常处理的文件备份工具我们将结合前几篇学的文件操作、os 模块和异常处理编写一个健壮的文件备份脚本。功能要求用户输入源文件路径和目标备份路径。程序检查源文件是否存在。程序尝试复制文件。处理各种可能的错误路径无效、权限不足、磁盘满等。import os import shutil def backup_file(): print( 欢迎使用安全文件备份工具 ) src input(请输入要备份的文件路径: ).strip() dst input(请输入备份目标路径含文件名: ).strip() try: # 1. 验证源文件 if not os.path.exists(src): raise FileNotFoundError(f源文件不存在{src}) if not os.path.isfile(src): raise ValueError(f路径存在但不是一个文件{src}) # 2. 验证目标目录 dst_dir os.path.dirname(dst) if dst_dir and not os.path.exists(dst_dir): # 询问是否创建目录 create input(f目标目录不存在 {dst_dir}是否自动创建(y/N): ) if create.lower() ! y: return # 用户取消 os.makedirs(dst_dir, exist_okTrue) # 3. 执行备份 print(f正在备份 {src} 到 {dst} ...) shutil.copy2(src, dst) # copy2 会保留元数据 print(✅ 备份成功) except PermissionError as e: print(f❌ 权限错误无法读取源文件或写入目标位置。) print(f 详情{e}) except FileNotFoundError as e: print(f❌ 文件未找到错误{e}) except OSError as e: # 捕获磁盘空间不足等操作系统级错误 print(f❌ 磁盘或系统错误可能是磁盘已满或路径过长。) print(f 详情{e}) except Exception as e: # 兜底异常 print(f❌ 发生未知错误{e}) finally: print(操作结束。) if __name__ __main__: backup_file() 结语通过本篇的学习你已经掌握了 Python 程序的“安全带”——异常处理。你学会了使用try-except-else-finally结构来捕获和处理错误。你理解了如何通过raise主动抛出异常。你掌握了自定义异常来处理业务逻辑错误。你学会了编写健壮的代码防止程序因意外输入而崩溃。现在你的程序已经具备了应对“意外”的能力。下一步建议回顾你之前写的代码比如计算器或文件操作脚本尝试加入异常处理机制看看程序的稳定性提升了多少。 下一篇预告《Python 零基础入门系列十四综合实战项目》这是本系列的收官之作。我们将把前 13 篇学到的所有知识语法、函数、模块、文件、异常等融合在一起从零开始开发一个完整的“个人记账管理系统”。敬请期待更新时间2026 年 3 月 30 日✍️作者书到用时方恨少!