【Python学习】Python异常处理及程序调试
目录前言第一部分Python异常处理1.1 异常的概念与常见类型1.1.1 什么是异常1.1.2 常见异常类型及场景1.2 异常处理的核心语法1.2.1 基本语法try-except1.2.2 捕获多个异常方式1多个except子句推荐针对性处理方式2单个except捕获多个异常用元组包裹1.2.3 try-except-else语句1.2.4 try-except-finally语句1.2.5 raise语句主动抛出异常1.3 自定义异常1.3.1 自定义异常的基本写法1.3.2 自定义异常的应用场景1.4 异常处理的最佳实践第二部分Python程序调试2.1 调试的核心思路2.2 常见调试方式从简单到复杂2.2.1 print打印调试最基础适合简单代码2.2.2 断言assert调试适合逻辑校验2.2.3 pdb调试命令行调试适合复杂代码方式1命令行启动直接调试整个脚本方式2脚本内启动在指定位置设置断点pdb常用调试命令必记pdb调试实操案例2.2.4 IDE调试最常用适合项目开发PyCharm调试步骤IDE调试的优势2.3 调试技巧与常见问题2.3.1 调试技巧2.3.2 常见调试问题及解决方法2.4 日志调试适合线上程序logging调试实操案例第三部分综合实操案例案例需求步骤1编写初始代码存在异常步骤2运行代码触发异常并调试步骤3优化代码添加异常处理步骤4再次调试验证优化效果总结前言在Python编程过程中无论代码编写多么严谨都难免出现错误。这些错误分为两类语法错误和异常运行时错误。语法错误是代码不符合Python语法规则导致的编写阶段即可发现而异常是代码语法正确但运行时因逻辑漏洞、资源不足等问题引发的错误若未妥善处理会导致程序崩溃。程序调试则是定位并解决这些错误尤其是异常的过程是每个Python开发者必备的核心技能。本教程将从异常处理的基础概念、语法用法到调试工具、调试技巧结合大量实操案例帮助大家系统掌握Python异常处理与程序调试提升代码的健壮性和可维护性。第一部分Python异常处理1.1 异常的概念与常见类型1.1.1 什么是异常异常Exception是Python程序运行时发生的非预期事件会打断程序的正常执行流程。例如除以零、访问不存在的变量、打开不存在的文件、网络连接失败等都会触发异常。Python中所有异常都继承自BaseException类其核心子类包括Exception普通异常和SystemExit程序退出、KeyboardInterrupt用户中断等。其中我们日常处理的异常主要是Exception类的子类。1.1.2 常见异常类型及场景以下是Python开发中最常遇到的异常类型结合具体场景说明方便大家快速识别ZeroDivisionError除以零异常当除数为0时触发。例如10 / 0。TypeError类型错误操作或函数应用于不适当类型的对象时触发。例如将字符串与数字相加abc 123、调用非可调用对象如给变量赋值后误当作函数调用。IndexError索引错误访问列表、元组等序列中不存在的索引时触发。例如lst [1,2,3]执行lst[3]。KeyError键错误访问字典中不存在的键时触发。例如dict1 {name: Python}执行dict1[age]。ValueError值错误传入的值类型正确但值不符合要求时触发。例如int(abc)字符串无法转换为整数、float(12a3)。FileNotFoundError文件未找到异常打开不存在的文件时触发。例如open(test.txt, r)若当前目录无test.txt文件。ImportError导入错误无法导入模块或模块中的对象时触发。例如导入不存在的模块import non_existent_module、模块中不存在的函数。AttributeError属性错误访问对象不存在的属性或方法时触发。例如str1 hello执行str1.append(world)字符串无append方法。1.2 异常处理的核心语法Python提供了try-except语句来捕获并处理异常避免程序崩溃。核心逻辑是将可能触发异常的代码放在try块中若发生异常执行except块中的处理逻辑若未发生异常跳过except块继续执行后续代码。1.2.1 基本语法try-excepttry: # 可能触发异常的代码核心执行代码 代码块1 except 异常类型1: # 捕获到“异常类型1”时执行的处理逻辑 代码块2案例处理除以零异常try: a 10 b 0 result a / b # 触发ZeroDivisionError except ZeroDivisionError: print(错误除数不能为0请重新输入除数) # 程序不会崩溃继续执行后续代码 print(程序继续运行...)运行结果错误除数不能为0请重新输入除数 程序继续运行...1.2.2 捕获多个异常实际开发中一段代码可能触发多种异常可通过两种方式捕获多个异常方式1多个except子句推荐针对性处理try: # 可能触发多种异常的代码 lst [1, 2, 3] result 10 / lst[3] # 可能触发IndexError或ZeroDivisionError except IndexError: print(错误索引超出范围) except ZeroDivisionError: print(错误除数不能为0) except Exception as e: # 捕获所有其他Exception类型异常兜底 print(f发生未知错误{e})说明except子句的顺序很重要子类异常必须放在父类异常前面如IndexError是Exception的子类需放在Exception前面否则子类异常会被父类异常捕获无法触发针对性处理。方式2单个except捕获多个异常用元组包裹try: lst [1, 2, 3] result 10 / lst[3] except (IndexError, ZeroDivisionError) as e: # 用元组包裹多个异常类型 print(f发生错误{e}) except Exception as e: print(f未知错误{e})注as e用于获取异常信息方便排查问题e是异常对象可直接打印输出错误详情。1.2.3 try-except-else语句else子句可选用于执行“当try块中没有发生异常时”的逻辑即try块执行成功后才会执行else块。try: a 10 b 2 result a / b except ZeroDivisionError: print(除数不能为0) else: print(f计算成功结果为{result}) # 只有try块无异常时执行 print(程序结束)运行结果计算成功结果为5.0 程序结束1.2.4 try-except-finally语句finally子句可选无论try块是否发生异常finally块都会执行常用于释放资源如关闭文件、关闭网络连接等避免资源泄露。try: # 尝试打开文件 f open(test.txt, r, encodingutf-8) content f.read() print(content) except FileNotFoundError: print(错误文件未找到) finally: # 无论是否发生异常都关闭文件 if f in locals(): # 判断文件对象是否存在 f.close() print(文件操作结束资源已释放)说明即使try块触发异常except块执行后依然会执行finally块若try块无异常执行完else若有后也会执行finally块。1.2.5 raise语句主动抛出异常除了Python自动触发的异常我们也可以通过raise语句主动抛出异常用于自定义异常场景如参数校验不通过。def check_age(age): if age 0 or age 120: # 主动抛出ValueError并指定异常信息 raise ValueError(年龄必须在0-120之间) return f年龄合法{age}岁 try: check_age(150) except ValueError as e: print(f参数错误{e})运行结果参数错误年龄必须在0-120之间也可以重新抛出异常不修改异常信息用于多层异常处理时向上传递异常try: check_age(150) except ValueError as e: print(捕获到年龄异常向上传递...) raise # 重新抛出当前异常不改变异常信息1.3 自定义异常Python内置的异常类型可覆盖大部分场景但在实际开发中我们可能需要定义符合业务场景的异常如“用户不存在异常”“订单超时异常”。自定义异常需继承Exception类不建议继承BaseException避免捕获到系统级异常。1.3.1 自定义异常的基本写法# 自定义异常类继承Exception class UserNotFoundError(Exception): # 重写__init__方法自定义异常信息 def __init__(self, user_id): self.user_id user_id # 调用父类的__init__方法传递异常信息 super().__init__(f用户ID{user_id} 不存在) # 使用自定义异常 def get_user(user_id): # 模拟业务逻辑用户ID不在列表中则抛出异常 user_list [101, 102, 103] if user_id not in user_list: raise UserNotFoundError(user_id) return f找到用户ID{user_id} try: get_user(104) except UserNotFoundError as e: print(f业务异常{e})运行结果业务异常用户ID104 不存在1.3.2 自定义异常的应用场景自定义异常主要用于业务逻辑的异常区分让代码更具可读性和可维护性。例如在电商系统中可定义OrderTimeoutError订单超时、StockInsufficientError库存不足等异常便于后续定位问题和处理不同的业务异常场景。1.4 异常处理的最佳实践不要捕获所有异常避免使用except Exception:兜底所有异常应针对性捕获具体异常否则会掩盖真正的错误如系统级异常难以排查。异常信息要明确捕获异常时打印具体的异常信息如print(f错误{e})避免只打印“发生错误”这类模糊提示。及时释放资源涉及文件、网络连接、数据库连接等资源操作时务必用finally块释放资源或使用上下文管理器with语句自动释放。自定义异常要规范自定义异常需继承Exception类命名规范以Error结尾并添加清晰的异常描述便于团队协作。避免过度使用异常异常用于处理“非预期错误”不要用异常替代正常的逻辑判断如判断参数是否为空应先做逻辑判断而非依赖异常捕获。第二部分Python程序调试程序调试是定位并解决代码中错误语法错误、异常的过程。调试的核心是“找到错误的位置、分析错误的原因、修改错误并验证”。Python提供了多种调试方式从简单的print打印到专业的调试工具适用于不同的场景。2.1 调试的核心思路无论使用哪种调试方式核心思路都是一致的定位错误确定错误发生的代码行、错误类型语法错误/异常。分析原因结合错误信息分析代码逻辑、变量值、输入输出是否符合预期。修改错误根据原因修改代码避免引入新的错误。验证结果运行代码确认错误已解决且程序运行符合预期。2.2 常见调试方式从简单到复杂2.2.1 print打印调试最基础适合简单代码print调试是最直观、最基础的调试方式通过打印关键变量的值、代码执行的流程判断错误位置。适用于代码量少、逻辑简单的场景。案例调试一个计算函数的错误def calculate(a, b): # 调试打印输入参数的值 print(f输入参数a{a}, b{b}) result a b # 假设此处逻辑错误应为a * b # 调试打印计算结果 print(f计算结果{result}) return result # 调用函数发现结果不符合预期 print(calculate(3, 4)) # 预期结果12实际输出7通过print打印可快速发现计算逻辑错误应为乘法而非加法修改后即可解决问题。优点简单、无需额外工具缺点代码中会残留大量print语句调试完成后需手动删除适合简单场景。2.2.2 断言assert调试适合逻辑校验断言assert是用于判断某个条件是否成立的语句若条件不成立会触发AssertionError异常中断程序运行并输出指定的提示信息。适用于调试阶段的逻辑校验如参数合法性、变量值范围。语法assert 条件表达式, 异常提示信息def calculate(a, b): # 断言a和b必须是正数否则触发AssertionError assert a 0 and b 0, a和b必须为正数 result a * b return result # 调用函数传入负数触发断言异常 print(calculate(-3, 4))运行结果触发异常AssertionError: a和b必须为正数说明断言仅用于调试阶段生产环境中可通过-O参数优化模式关闭断言运行时忽略assert语句避免影响程序性能。2.2.3 pdb调试命令行调试适合复杂代码pdb是Python内置的命令行调试工具可实现断点设置、单步执行、查看变量值等功能适用于代码量较大、逻辑复杂的场景。pdb调试有两种启动方式方式1命令行启动直接调试整个脚本在终端中执行python -m pdb 脚本文件名.py进入pdb调试模式。方式2脚本内启动在指定位置设置断点在脚本中导入pdb模块在需要调试的位置添加pdb.set_trace()运行脚本时会自动进入调试模式。pdb常用调试命令必记命令功能说明h / help查看所有调试命令的帮助n / next单步执行跳过函数调用不进入函数内部s / step单步执行进入函数内部逐行执行b / break 行号在指定行设置断点如b 10在第10行设置断点b / break 函数名在指定函数的入口设置断点c / continue继续执行直到遇到下一个断点或程序结束p / print 变量名打印指定变量的值如p a打印变量a的值l / list查看当前调试位置附近的代码默认显示11行q / quit退出pdb调试模式r / return执行到当前函数结束并返回结果pdb调试实操案例编写一个有错误的函数用pdb调试定位错误import pdb def add_list(lst): total 0 pdb.set_trace() # 设置断点运行到此处进入调试模式 for num in lst: total num # 假设错误应为total * num return total # 调用函数预期结果1*2*36实际结果6若错误则为6此处模拟错误 print(add_list([1, 2, 3]))调试步骤运行脚本自动进入pdb调试模式提示(pdb)。输入l查看当前代码位置确认断点在for循环前。输入n单步执行到for循环输入p total查看total初始值为0。继续输入n逐行执行循环观察total的值变化第一次循环后total1第二次3第三次6若逻辑错误为加法此处会发现结果不符合预期。发现循环内的逻辑错误应为乘法修改代码后退出调试输入q重新运行验证。2.2.4 IDE调试最常用适合项目开发日常开发中最常用的调试方式是通过IDE如PyCharm、VS Code自带的调试工具图形化界面操作无需记忆命令效率更高。以下以PyCharm为例讲解IDE调试的核心操作。PyCharm调试步骤设置断点在需要调试的代码行左侧点击鼠标左键出现红色圆点断点断点可设置在任意行通常设置在异常可能发生的位置、关键逻辑处。启动调试点击PyCharm右上角的“调试”按钮绿色虫子图标或右键点击脚本选择“Debug 脚本名.py”启动调试模式。调试操作调试模式启动后程序会停在第一个断点处此时可通过工具栏的按钮进行操作单步执行F8逐行执行代码跳过函数调用类似pdb的n命令。步入函数F7进入当前执行的函数内部逐行调试类似pdb的s命令。步出函数ShiftF8执行完当前函数的剩余代码返回上一层调用类似pdb的r命令。继续执行F9继续执行代码直到遇到下一个断点或程序结束类似pdb的c命令。停止调试CtrlF2终止调试模式。查看变量调试时PyCharm右侧会显示“Variables”面板实时显示当前作用域内的所有变量及其值可直接查看、修改变量值快速定位错误。添加监视若需要重点关注某个变量可在“Watches”面板中添加变量实时监视其值的变化右键选择“Add Watch”。IDE调试的优势图形化界面操作简单实时查看变量值无需手动打印支持断点条件设置仅当满足某个条件时程序才停在断点处支持多线程、多进程调试适合复杂项目。2.3 调试技巧与常见问题2.3.1 调试技巧断点精准设置不要在每一行都设置断点重点设置在异常触发点、函数入口/出口、关键逻辑判断处提高调试效率。利用条件断点在IDE中设置断点时可添加条件如“num 0”只有当条件满足时程序才会停在断点处适合排查特定场景下的错误。分步调试变量监视对于复杂逻辑采用“分步执行实时查看变量”的方式逐步缩小错误范围避免盲目修改代码。结合日志调试对于无法实时调试的场景如线上程序可通过日志模块logging记录关键信息变量值、执行流程事后分析日志定位错误。简化代码调试若代码逻辑过于复杂可临时简化代码注释掉无关逻辑聚焦于错误相关的代码调试完成后再恢复。2.3.2 常见调试问题及解决方法断点不生效检查是否启动了“调试模式”而非普通运行检查断点是否设置在可执行行如注释行、空行无法设置有效断点检查IDE是否开启了“跳过断点”模式。变量值无法查看检查变量是否在当前作用域内如函数内的局部变量在函数外无法查看检查调试是否停在变量定义之后若变量未定义无法查看其值。程序调试时卡顿可能是代码中存在死循环可通过“暂停调试”CtrlPause查看当前执行的代码行定位死循环位置。线上程序无法调试线上程序不可直接调试可通过添加日志、捕获异常并记录详细信息如异常堆栈线下复现问题后再调试。2.4 日志调试适合线上程序对于线上运行的程序无法实时调试此时可通过Python的logging模块记录日志包含变量值、执行流程、异常信息等事后通过日志分析错误原因。logging模块的核心优势可设置日志级别DEBUG、INFO、WARNING、ERROR、CRITICAL灵活控制日志输出可将日志写入文件便于长期保存和分析支持格式化日志信息包含时间、日志级别、代码位置等。logging调试实操案例import logging # 配置日志设置日志级别、日志格式、输出方式控制台文件 logging.basicConfig( levellogging.DEBUG, # 日志级别DEBUG最低级别记录所有日志 format%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s, # 日志格式 handlers[ logging.FileHandler(debug.log), # 日志写入文件 logging.StreamHandler() # 日志输出到控制台 ] ) def calculate(a, b): logging.debug(f开始计算输入参数a{a}, b{b}) # 调试日志 try: result a / b logging.info(f计算成功结果{result}) # 信息日志 return result except ZeroDivisionError as e: logging.error(f计算失败{e}, exc_infoTrue) # 错误日志exc_infoTrue记录异常堆栈 raise # 调用函数 calculate(10, 0)运行后会生成debug.log文件同时控制台输出日志日志中包含异常堆栈信息可清晰看到错误发生的代码行和原因便于事后排查。第三部分综合实操案例结合异常处理和程序调试完成一个“文件读写”的综合案例模拟实际开发中的问题及解决过程。案例需求编写一个Python程序实现以下功能读取指定路径的文本文件文件内容为数字每行一个数字。计算文件中所有数字的总和。将计算结果写入一个新的文本文件中。妥善处理可能出现的异常文件未找到、文件内容不是数字、权限不足等。若出现异常通过调试定位并解决问题。步骤1编写初始代码存在异常def read_and_calculate(file_path): # 读取文件 f open(file_path, r, encodingutf-8) content f.readlines() f.close() # 计算总和 total 0 for line in content: num int(line) # 若行内容不是数字会触发ValueError total num # 写入结果 with open(result.txt, w, encodingutf-8) as f: f.write(f所有数字的总和{total}) return total # 调用函数 read_and_calculate(numbers.txt)步骤2运行代码触发异常并调试假设当前目录不存在numbers.txt文件运行代码会触发FileNotFoundError程序崩溃。调试过程通过异常信息定位错误位置open(file_path, r)行提示文件未找到。分析原因文件路径错误或文件不存在。修改方案添加异常捕获提示用户检查文件路径同时使用finally块或with语句确保文件资源释放。步骤3优化代码添加异常处理def read_and_calculate(file_path): try: # 读取文件使用with语句自动释放资源 with open(file_path, r, encodingutf-8) as f: content f.readlines() # 计算总和 total 0 for idx, line in enumerate(content, 1): try: num int(line.strip()) # 去除换行符避免空格导致的错误 total num except ValueError: print(f警告第{idx}行内容不是有效数字已跳过内容{line.strip()}) # 写入结果 with open(result.txt, w, encodingutf-8) as f: f.write(f所有数字的总和{total}) print(f计算完成总和已写入result.txt文件总和为{total}) return total except FileNotFoundError: print(f错误文件 {file_path} 未找到请检查文件路径是否正确) except PermissionError: print(f错误权限不足无法读取文件 {file_path} 或写入result.txt文件) except Exception as e: print(f发生未知错误{e}) # 记录异常堆栈便于调试 import traceback traceback.print_exc() # 调用函数 read_and_calculate(numbers.txt)步骤4再次调试验证优化效果场景1numbers.txt不存在 → 触发FileNotFoundError提示用户检查路径程序不崩溃。场景2numbers.txt存在但部分行不是数字 → 触发ValueError提示用户跳过无效行继续计算程序正常运行。场景3权限不足 → 触发PermissionError提示用户权限问题程序不崩溃。通过异常处理程序的健壮性大幅提升通过调试定位并解决了初始代码中的资源释放、异常未处理等问题。总结Python异常处理的核心是“预防崩溃、优雅处理”通过try-except语句捕获异常结合else、finally、raise等语法实现对异常的精准控制自定义异常则能适配业务场景提升代码可读性。程序调试的核心是“定位错误、解决错误”从基础的print打印、断言到专业的pdb命令行调试、IDE图形化调试再到线上的日志调试需根据场景选择合适的方式。调试的关键是“耐心、细致”通过逐步排查找到错误的根源避免盲目修改代码。掌握异常处理与程序调试能有效提升代码的健壮性和可维护性减少程序崩溃的概率是每个Python开发者必备的技能。建议多动手实操结合案例练习逐步熟练运用各类异常处理语法和调试工具。