异常处理概述
异常处理是指程序在运行过程中遇到错误时,通过特定的语法结构来捕获、处理错误,防止程序意外崩溃
在编写程序时,我们无法预测所有可能发生的错误:文件可能不存在、网络可能断开、用户可能输入非法数据。如果不对这些情况进行处理,程序就会直接崩溃退出。
来看一个没有异常处理的例子:
1 2 3 4 5 6 7 num = int (input ("请输入一个数字:" )) result = 100 / num print (f"100 / {num} = {result} " )
通过异常处理,我们可以让程序"优雅地"应对这些意外情况:
1 2 3 4 5 6 7 """ 异常处理的核心思路: 1. 尝试执行可能出现问题的代码 2. 如果出现错误,捕获它 3. 根据错误类型做出相应的处理 4. 程序继续运行,不会崩溃 """
常见异常类型
Python 内置了丰富的异常类型,不同错误对应不同的异常类,了解它们有助于精确地处理各种错误
Python 中常见的异常类型:
异常类
触发条件
示例
NameError
使用了未定义的变量
print(unknown_var)
TypeError
操作类型不匹配
"hello" + 5
ValueError
值不合法
int("abc")
ZeroDivisionError
除数为零
100 / 0
IndexError
列表索引越界
[1,2,3][10]
KeyError
字典键不存在
{"a":1}["b"]
FileNotFoundError
文件不存在
open("no.txt")
AttributeError
对象没有该属性
"str".append()
ImportError
导入模块失败
import not_exist
SyntaxError
语法错误
if True print()
IndentationError
缩进错误
混用空格和Tab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
1 2 3 4 5 6 7 8 9 10 11 12 """ 所有内置异常都继承自 BaseException 程序逻辑相关的异常继承自 Exception 了解继承关系有助于理解异常捕获的匹配规则 """ print (ZeroDivisionError.__bases__) print (ArithmeticError.__bases__) print (FileNotFoundError.__bases__) print (OSError.__bases__)
try-except 基础用法
使用 try-except 结构捕获异常:把可能出错的代码放入 try 块,在 except 块中编写错误处理逻辑
基本语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 """ try-except 基本结构: try: # 可能发生异常的代码 risky_operation() except 异常类型: # 发生异常时的处理代码 handle_error() """ try : a = int (input ("被除数:" )) b = int (input ("除数:" )) result = a / b print (f"结果:{result} " ) except ZeroDivisionError: print ("错误:除数不能为 0" ) except ValueError: print ("错误:请输入有效的整数" ) print ("程序继续运行..." )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def unsafe_division (): a = int (input ("被除数:" )) b = int (input ("除数:" )) return a / b def safe_division (): try : a = int (input ("被除数:" )) b = int (input ("除数:" )) return a / b except ZeroDivisionError: print ("除数不能为 0" ) except ValueError: print ("请输入有效整数" ) return None result = safe_division() print (f"安全除法的结果:{result} " )
捕获异常并获取错误信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 """ 使用 as 关键字将异常对象绑定到一个变量 通过该变量可以获取具体的错误信息 """ try : num = int ("abc" ) except ValueError as e: print (f"捕获到 ValueError" ) print (f"错误信息:{e} " ) print (f"异常类型:{type (e).__name__} " )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def read_file_safely (filename ): try : with open (filename, 'r' , encoding='utf-8' ) as f: return f.read() except FileNotFoundError as e: print (f"❌ 文件不存在:{filename} " ) print (f" 详细信息:{e} " ) except PermissionError as e: print (f"❌ 没有权限读取:{filename} " ) print (f" 详细信息:{e} " ) except Exception as e: print (f"❌ 发生了未知错误:{e} " ) return None content = read_file_safely("不存在的文件.txt" ) print (f"读取结果:{content} " )
捕获所有异常(慎用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 """ except 后面不指定异常类型,会捕获所有异常 但这种方式不推荐: - 它会捕获 SyntaxError、KeyboardInterrupt 等不该捕获的异常 - 会掩盖程序中真正的逻辑错误 - 调试时会很难定位问题 更好的做法:至少指定 except Exception,这排除了系统级异常 """ try : risky_code() except : print ("出错了" ) try : risky_code() except Exception as e: print (f"出错了:{e} " )
完整的异常处理结构
try-except-else-finally 构成了完整的异常处理流程,每个子句各司其职
四种子句的角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 """ 完整的异常处理结构: try: # 尝试执行的代码(监控区) pass except 异常类型: # 发生异常时执行(处理区) pass else: # 没有发生异常时执行(成功区) pass finally: # 无论是否发生异常都执行(清理区) pass """ def load_config (filename ): config = {} f = None try : f = open (filename, 'r' , encoding='utf-8' ) content = f.read() for line in content.strip().split('\n' ): key, value = line.split('=' ) config[key.strip()] = value.strip() except FileNotFoundError: print (f"配置文件 {filename} 不存在,使用默认配置" ) except ValueError as e: print (f"配置格式错误:{e} " ) else : print (f"配置文件 {filename} 加载成功,共 {len (config)} 项" ) finally : if f: f.close() print ("文件已关闭" ) return config settings = load_config("config.ini" )
else 子句的使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 """ else 子句只在 try 块没有发生异常时执行 使用 else 而非把代码直接放在 try 块中,有几个好处: 1. 语义更清晰:这些代码只在"一切正常"时运行 2. 避免意外捕获 else 代码中抛出的异常 3. 减小 try 块的代码范围,明确哪些代码需要被监控 """ def process_data_bad (raw_data ): try : num = int (raw_data) result = 100 / num print (f"计算成功,结果:{result} " ) log_result(result) except (ValueError, ZeroDivisionError) as e: print (f"输入有误:{e} " ) def process_data_good (raw_data ): try : num = int (raw_data) result = 100 / num except ValueError: print ("请输入有效的数字" ) except ZeroDivisionError: print ("除数不能为 0" ) else : print (f"计算成功,结果:{result} " ) log_result(result)
finally 子句的使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 """ finally 子句无论是否发生异常都会执行 典型用途: - 关闭文件/数据库连接/网络连接 - 释放锁 - 恢复环境状态 """ def write_data (filename, data ): f = open (filename, 'w' , encoding='utf-8' ) try : for item in data: f.write(str (item) + '\n' ) finally : f.close() print (f"文件 {filename} 已关闭" ) def connect_server (address ): import time print (f"正在连接 {address} ..." ) start = time.time() try : time.sleep(1 ) print ("连接成功" ) except ConnectionError as e: print (f"连接失败:{e} " ) else : print ("正在进行数据传输..." ) finally : elapsed = time.time() - start print (f"操作耗时:{elapsed:.1 f} 秒" ) print ("连接已关闭(如有必要)" )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def demo_finally (): try : print ("try 块" ) return "try 的返回值" except : print ("except 块" ) finally : print ("finally 块 — 无论如何都会执行" ) print ("这行永远不会执行" ) result = demo_finally() print (f"函数返回值:{result} " )
捕获多种异常
根据不同的异常类型分别处理,也可以用元组一次捕获多种异常,还可以通过异常继承关系实现层级捕获
多个 except 子句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 """ 一个 try 可以对应多个 except 子句 按顺序匹配:从上到下,匹配到第一个符合的 except 后就停止 注意:子类异常要放在父类异常前面! """ def safe_operation (data ): try : value = int (data) result = 100 / value items = [10 , 20 , 30 ] print (f"取出的元素:{items[value]} " ) return result except ValueError: print ("ValueError:数据无法转换为整数" ) except ZeroDivisionError: print ("ZeroDivisionError:不能除以零" ) except IndexError: print ("IndexError:索引超出了列表范围" ) except Exception as e: print (f"其他未预料的异常:{e} " ) safe_operation("abc" ) safe_operation("0" ) safe_operation("5" ) safe_operation("1" )
用元组一次捕获多种异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 """ 当多种异常的处理方式相同时,可以用元组将它们放在一个 except 中 """ def process_input_verbose (data ): try : num = int (data) return 100 / num except ValueError: print ("输入不合法,请重新输入" ) return None except ZeroDivisionError: print ("输入不合法,请重新输入" ) return None def process_input_clean (data ): try : num = int (data) return 100 / num except (ValueError, ZeroDivisionError) as e: print (f"输入不合法({e} ),请重新输入" ) return None process_input_clean("abc" ) process_input_clean("0" )
异常继承与捕获顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 """ 异常的捕获是"向上兼容"的——捕获父类异常会同时捕获其所有子类异常 因此,子类异常必须放在父类异常的前面 """ def wrong_order (): try : f = open ("nofile.txt" ) except Exception as e: print (f"Exception 捕获了:{e} " ) except FileNotFoundError as e: print (f"FileNotFoundError 捕获了:{e} " ) def right_order (): try : f = open ("nofile.txt" ) except FileNotFoundError as e: print (f"文件没找到:{e} " ) except OSError as e: print (f"其他 I/O 错误:{e} " ) except Exception as e: print (f"其他未预料的错误:{e} " )
异常的传播
当异常在一个函数内部没有被捕获时,它会向上传播到调用它的函数,直到被捕获或导致程序终止
异常传播机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 """ 异常传播(冒泡)规则: 1. 函数内部发生异常且未被捕获 → 传播到调用方 2. 调用方未捕获 → 继续向上传播 3. 一直传播到全局作用域仍未被捕获 → 程序终止并打印 Traceback """ def level3 (): print ("level3 开始" ) result = 1 / 0 print ("level3 结束" ) def level2 (): print ("level2 开始" ) level3() print ("level2 结束" ) def level1 (): print ("level1 开始" ) try : level2() except ZeroDivisionError: print ("level1 捕获了 ZeroDivisionError!" ) print ("level1 结束" ) level1()
在哪里捕获异常?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 """ 原则:在"知道如何处理"的层级捕获异常 - 底层函数:让异常自然传播,不要随意吞掉 - 中间层:可以包装异常,添加上下文信息后重新抛出 - 顶层:统一捕获并处理(日志记录、用户提示等) """ def parse_line (line ): """解析一行数据,格式:name:score""" name, score_str = line.split(':' ) return name, int (score_str) def load_scores (filename ): """从文件加载所有成绩""" scores = {} try : with open (filename, 'r' , encoding='utf-8' ) as f: for i, line in enumerate (f, 1 ): try : name, score = parse_line(line.strip()) scores[name] = score except (ValueError, TypeError) as e: raise ValueError(f"第{i} 行格式错误:'{line.strip()} '" ) from e except FileNotFoundError: raise FileNotFoundError(f"成绩文件 '{filename} ' 不存在" ) return scores def main (): try : scores = load_scores("scores.txt" ) print (f"加载了 {len (scores)} 条成绩" ) except (FileNotFoundError, ValueError) as e: print (f"程序出错:{e} " ) print ("请检查文件是否存在且格式正确" ) except Exception as e: print (f"未知错误:{e} " )
抛出异常
使用 raise 关键字手动抛出异常,可以在特定条件下主动触发错误处理流程
raise 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 """ raise 异常类(错误信息):主动抛出一个异常 使用场景: - 输入参数不合法 - 业务规则被违反 - 状态不一致 """ def set_age (age ): """设置年龄,必须为 0-150 之间的整数""" if not isinstance (age, int ): raise TypeError(f"年龄必须是整数,而输入了 {type (age).__name__} " ) if age < 0 or age > 150 : raise ValueError(f"年龄超出合理范围:{age} (应为 0-150)" ) print (f"年龄已设置为:{age} " ) return age set_age(25 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def transfer_money (sender, receiver, amount ): """转账操作""" if amount <= 0 : raise ValueError(f"转账金额必须大于 0,当前:{amount} " ) if sender == receiver: raise ValueError("不能给自己转账" ) if amount > sender["balance" ]: raise ValueError( f"余额不足:需要 {amount} 元,当前余额 {sender['balance' ]} 元" ) sender["balance" ] -= amount receiver["balance" ] += amount print (f"转账成功:{sender['name' ]} → {receiver['name' ]} ,金额 {amount} 元" ) alice = {"name" : "Alice" , "balance" : 1000 } bob = {"name" : "Bob" , "balance" : 500 } try : transfer_money(alice, bob, 1200 ) except ValueError as e: print (f"转账失败:{e} " ) transfer_money(alice, bob, 200 )
重新抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 """ 在 except 块中可以用 raise(不带参数)重新抛出当前异常 这在"记录日志但不吞异常"的场景中很有用 """ def process_with_logging (data ): try : result = risky_operation(data) except Exception as e: print (f"[ERROR] 处理数据 '{data} ' 时发生异常:{e} " ) raise return result def load_user_config (user_id ): try : user_data = database.query(user_id) except DatabaseError as e: raise ConfigLoadError(f"加载用户 {user_id} 配置失败" ) from e
自定义异常
当内置异常无法准确表达业务错误时,可以创建自定义异常类,继承自 Exception
创建简单的自定义异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 """ 自定义异常类只需要继承 Exception(或其子类) 通常只需定义一个类体,可以添加自定义属性 """ class InsufficientBalanceError (Exception ): """余额不足异常""" pass class InvalidPasswordError (Exception ): """密码格式错误异常""" pass class AccountLockedError (Exception ): """账户被锁定异常""" pass def withdraw (account, amount ): """取款操作""" if account["locked" ]: raise AccountLockedError(f"账户 {account['name' ]} 已被锁定" ) if amount <= 0 : raise ValueError("取款金额必须大于 0" ) if amount > account["balance" ]: raise InsufficientBalanceError( f"余额不足:需要 {amount} 元,可用 {account['balance' ]} 元" ) account["balance" ] -= amount print (f"取款成功:{amount} 元,剩余 {account['balance' ]} 元" )
带属性的自定义异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 """ 自定义异常可以添加额外的属性来携带更多错误信息 这有助于调用方更精确地处理异常 """ class ValidationError (Exception ): """数据校验异常,携带校验失败的字段和原因""" def __init__ (self, field, value, reason ): self .field = field self .value = value self .reason = reason message = f"字段 '{field} ' 的值 '{value} ' 不合法:{reason} " super ().__init__(message) def validate_user (data ): """校验用户输入""" if not data.get("username" ): raise ValidationError("username" , data.get("username" ), "用户名不能为空" ) password = data.get("password" , "" ) if len (password) < 6 : raise ValidationError("password" , "***" , "密码长度不能少于 6 位" ) age = data.get("age" ) if age is not None and (age < 0 or age > 150 ): raise ValidationError("age" , age, "年龄应在 0-150 之间" ) print ("数据校验通过" ) test_data = {"username" : "admin" , "password" : "123" , "age" : 200 } try : validate_user(test_data) except ValidationError as e: print (f"校验失败:" ) print (f" 字段:{e.field} " ) print (f" 值:{e.value} " ) print (f" 原因:{e.reason} " )
自定义异常的层级结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 """ 对于较大的项目,建立异常层级结构有助于分类处理 例如一个简单的电商系统: """ class ECommerceError (Exception ): """电商系统基础异常""" pass class PaymentError (ECommerceError ): """支付相关异常""" pass class CreditCardDeclinedError (PaymentError ): """信用卡被拒""" pass class InsufficientFundsError (PaymentError ): """余额不足""" pass class InventoryError (ECommerceError ): """库存相关异常""" pass class OutOfStockError (InventoryError ): """库存不足""" pass def checkout (cart, payment_method ): try : validate_stock(cart) process_payment(payment_method, cart["total" ]) except OutOfStockError as e: print (f"库存不足:{e} ,建议移除缺货商品" ) except PaymentError as e: print (f"支付失败:{e} ,请更换支付方式" ) except ECommerceError as e: print (f"结账失败:{e} " )
断言
assert 语句用于在开发调试阶段快速检查"不应该发生"的情况,生产环境中可以全局关闭
assert 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 """ assert 条件, 错误信息: - 条件为 True:什么都不发生,程序继续 - 条件为 False:抛出 AssertionError 并显示错误信息 """ def calculate_average (scores ): """计算平均分,scores 不能为空列表""" assert len (scores) > 0 , "成绩列表不能为空" return sum (scores) / len (scores) print (calculate_average([85 , 90 , 78 ]))
1 2 3 4 5 6 7 8 9 10 11 12 13 def divide_safely (a, b ): """除法运算,确保除数和结果都合法""" assert b != 0 , "除数不能为 0" result = a / b import math assert math.isfinite(result), f"计算结果不合法:{result} " return result
assert 与 raise 的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 """ assert vs raise: ┌──────────┬─────────────────┬──────────────────────┐ │ 方式 │ 典型用途 │ 可被关闭? │ ├──────────┼─────────────────┼──────────────────────┤ │ assert │ 调试时检查逻辑 │ 是(python -O 关闭) │ │ raise │ 任何时候检查输入 │ 否 │ └──────────┴─────────────────┴──────────────────────┘ 原则: - 检查用户输入、外部数据 → 用 raise(永远需要校验) - 检查程序内部逻辑错误("不可能发生"的事) → 用 assert(只用于调试) """ def set_volume (level ): if not 0 <= level <= 100 : raise ValueError(f"音量应在 0-100,输入了 {level} " ) print (f"音量:{level} " ) def _sort_helper (items ): """内部排序辅助函数,调用方已保证 items 是列表""" assert isinstance (items, list ), "内部错误:_sort_helper 需要列表参数" items.sort()
综合练习
将所学知识融会贯通,通过实际案例巩固异常处理的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 """ 实现一个健壮的用户输入验证系统,能够处理各种输入异常 """ def get_user_age (): """反复提示用户输入年龄,直到得到合法值""" while True : try : age_str = input ("请输入您的年龄(0-150):" ) age = int (age_str) if age < 0 or age > 150 : raise ValueError(f"年龄 {age} 不在 0-150 范围内" ) except ValueError as e: print (f"输入无效:{e} ,请重新输入" ) except KeyboardInterrupt: print ("\n用户取消了输入" ) return None except EOFError: print ("\n输入流已关闭" ) return None else : print (f"您的年龄是:{age} " ) return age
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 """ 从 JSON 配置文件读取配置,如果文件不存在或格式错误则使用默认配置 并具备安全写入的能力 """ import jsonclass ConfigManager : """配置文件管理器,带有完整的异常处理""" DEFAULT_CONFIG = { "host" : "localhost" , "port" : 8080 , "debug" : False , "max_connections" : 10 } def __init__ (self, config_file ): self .config_file = config_file self .config = {} def load (self ): """加载配置,失败时使用默认值""" try : with open (self .config_file, 'r' , encoding='utf-8' ) as f: loaded = json.load(f) except FileNotFoundError: print (f"[提示] 配置文件 {self.config_file} 不存在,使用默认配置" ) self .config = self .DEFAULT_CONFIG.copy() return self .config except json.JSONDecodeError as e: print (f"[错误] 配置文件格式错误:{e} " ) print (f"[提示] 将使用默认配置" ) self .config = self .DEFAULT_CONFIG.copy() return self .config except Exception as e: print (f"[错误] 加载配置时发生意外错误:{e} " ) self .config = self .DEFAULT_CONFIG.copy() return self .config else : self .config = {**self .DEFAULT_CONFIG, **loaded} print (f"[成功] 配置文件 {self.config_file} 加载完成" ) return self .config def save (self ): """保存配置到文件""" try : with open (self .config_file, 'w' , encoding='utf-8' ) as f: json.dump(self .config, f, indent=2 , ensure_ascii=False ) except PermissionError: print (f"[错误] 没有权限写入 {self.config_file} " ) return False except Exception as e: print (f"[错误] 保存配置失败:{e} " ) return False else : print (f"[成功] 配置已保存到 {self.config_file} " ) return True def get (self, key ): """安全地获取配置项""" try : return self .config[key] except KeyError: print (f"[警告] 配置项 '{key} ' 不存在" ) return None def set (self, key, value ): """设置配置项""" if key not in self .DEFAULT_CONFIG: print (f"[警告] '{key} ' 不是已知的配置项,已添加" ) self .config[key] = value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 """ 实现一个带重试机制的函数,遇到临时性错误时自动重试 """ import timeimport randomclass NetworkError (Exception ): """网络异常""" pass class TimeoutError (NetworkError ): """超时异常""" pass class ServerError (NetworkError ): """服务器异常""" pass def simulate_unreliable_request (): """模拟不稳定的网络请求(随机失败)""" rand = random.random() if rand < 0.3 : raise TimeoutError("连接超时" ) elif rand < 0.6 : raise ServerError("服务器返回 500 错误" ) elif rand < 0.75 : raise NetworkError("网络不可达" ) return {"status" : 200 , "data" : "请求成功!" } def request_with_retry (max_retries=3 , delay=1 ): """ 带重试机制的网络请求 max_retries: 最大重试次数 delay: 每次重试的等待秒数 """ for attempt in range (1 , max_retries + 1 ): try : print (f"第 {attempt} 次尝试..." ) result = simulate_unreliable_request() except TimeoutError as e: print (f" ⚠ 超时:{e} " ) if attempt < max_retries: print (f" ↩ 等待 {delay} 秒后重试..." ) time.sleep(delay) except ServerError as e: print (f" ⚠ 服务器错误:{e} " ) if attempt < max_retries: print (f" ↩ 等待 {delay * 2 } 秒后重试..." ) time.sleep(delay * 2 ) except NetworkError as e: print (f" ❌ 网络错误:{e} (不重试)" ) return None else : print (f" ✅ {result['data' ]} " ) return result["data" ] print (f"❌ 已重试 {max_retries} 次,全部失败" ) return None
异常处理是编写健壮 Python 程序的核心技能。掌握 try-except-else-finally 的完整结构、理解异常的传播机制、善用自定义异常来清晰表达业务错误,以及合理使用断言进行调试——这些能力能让你写出既不容易崩溃、又容易排查问题的代码。记住原则:在知道如何处理的地方捕获异常,不确定时就让异常向上传播。
Python Study Note 系列文章
Python Study Note Python 函数使用(四) Python 判断语句(二) Python 基础语法(一) Python 异常处理(七) Python 循环语句(三) Python 数据容器(五) Python 文件基础操作(六) Python 模块与包(八) Python 类型注解(十) Python 面向对象(九)重点 Python 高阶技巧(十一)