Try和expect的正确使用方式

Try和expect的正确使用方式 什么是异常Exception程序运行时发生的错误事件若不处理会导致程序崩溃⚠️异常 vs 错误Python 中有两类问题语法错误SyntaxError在代码运行前就被发现异常Exception在运行时才会触发。try/except 只能处理运行时异常语法错误必须手动修复代码。常见异常类型一览ValueError值类型正确但内容不合法int(abc)TypeError操作或函数作用于错误类型5 5IndexError序列索引超出范围[1,2,3][10]KeyError字典中键不存在{}[key]ZeroDivisionError除数为零10 / 0FileNotFoundError文件或目录不存在open(no.txt)AttributeError对象没有该属性或方法None.split()ImportError导入模块失败import xyz基本语法结构try/except 完整语法格式与各子句功能 完整语法格式try:放置可能引发异常的代码块程序会先尝试执行这里except:当 try 块中发生异常时执行可指定捕获特定异常类型else:没有发生异常时才执行可选用于放置正常逻辑finally:无论是否发生异常都会执行可选常用于资源清理basic_syntax.py# 最简单的 try/except try: result 10 / 0 # 这里会触发 ZeroDivisionError except ZeroDivisionError: print(❌ 错误除数不能为零) # 输出❌ 错误除数不能为零 # 获取异常信息 try: num int(hello) # ValueError except ValueError as e: # as e 获取异常对象 print(f❌ 异常类型{type(e).__name__}) print(f❌ 异常信息{e}) # 输出 # 异常类型ValueError # 异常信息invalid literal for int() with base 10: hello执行流程图解通过流程图理解 try / except / else / finally 的执行顺序try → 若有异常走 except → 若无异常走 else → 最终无论如何都执行 finally1 try 块开始执行Python 逐行执行 try 块中的代码一旦某行抛出异常立即跳转到对应的 except 块try 块剩余代码不再执行2 异常匹配 exceptPython 从上到下依次检查 except 子句找到第一个匹配的异常类型就执行若无匹配则异常继续向上层传播3 else无异常时才执行只有 try 块中没有发生任何异常时才执行 else用于放置依赖 try 成功执行的逻辑4 finally必定执行无论是否发生异常finally 块始终执行常用于关闭文件、释放数据库连接、清理资源flow_demo.py — 演示完整执行顺序def divide(a, b): print([try] 开始执行...) try: result a / b print(f[try] 计算结果 {result}) except ZeroDivisionError as e: print(f[except] 捕获异常{e}) else: print([else] 没有发生异常执行 else) finally: print([finally] 无论如何都会执行\n) # 测试1正常情况b ! 0 divide(10, 2) # [try] 开始执行... # [try] 计算结果 5.0 # [else] 没有发生异常执行 else # [finally] 无论如何都会执行 # 测试2异常情况b 0 divide(10, 0) # [try] 开始执行... # [except] 捕获异常division by zero # [finally] 无论如何都会执行多个 except 子句针对不同异常类型分别处理精准捕获原则精确优先父类靠后子类异常要写在父类之前否则会被父类先匹配到。例如 FileNotFoundError 是 OSError 的子类应写在 OSError 前面。multi_except.py# 捕获多个不同类型的异常 def safe_input_calc(user_input, divisor): try: num int(user_input) # 可能 ValueError lst [1, 2, 3] item lst[num] # 可能 IndexError result item / divisor # 可能 ZeroDivisionError return result except ValueError: print(⚠️ 请输入有效的整数) except IndexError: print(⚠️ 索引超出列表范围) except ZeroDivisionError: print(⚠️ 除数不能为零) except Exception as e: # 兜底捕获其他所有异常 print(f❌ 未知错误{e}) safe_input_calc(abc, 1) # ⚠️ 请输入有效的整数 safe_input_calc(9, 1) # ⚠️ 索引超出列表范围 safe_input_calc(1, 0) # ⚠️ 除数不能为零 # 一个 except 捕获多种异常元组形式 try: x int(hello) except (ValueError, TypeError) as e: print(f值或类型错误{e})写法说明示例except ExceptionType:捕获指定类型异常except ValueError:except ExceptionType as e:捕获并获取异常对象except ValueError as e:except (T1, T2) as e:同时捕获多种类型except (ValueError, TypeError):except Exception as e:捕获所有标准异常兜底⚠️ 不推荐滥用except:捕获所有内容含 SystemExit❌ 强烈不推荐✅else 与 finally 子句else 在无异常时执行finally 永远执行用于资源清理file_handling.py — finally 资源清理经典案例# 文件操作中 finally 的关键作用 def read_file(filepath): f None try: f open(filepath, r, encodingutf-8) content f.read() print(f✅ 读取成功共 {len(content)} 个字符) return content except FileNotFoundError: print(f❌ 文件不存在{filepath}) return None except PermissionError: print(❌ 没有权限读取此文件) return None else: print( else文件读取完全正常) # 无异常时执行 finally: if f: # 无论如何都关闭文件 f.close() print( finally文件已安全关闭) # 更推荐写法with 语句自动关闭文件 def read_file_better(filepath): try: with open(filepath, r, encodingutf-8) as f: return f.read() # with 自动处理 close() except FileNotFoundError: print(❌ 文件不存在) return Noneelse 的使用场景将成功后的逻辑与 try 分离代码更清晰避免 try 块过长、意外捕获 else 中的异常只在真正没有异常时才执行数据库写入等操作finally 的使用场景关闭文件句柄、网络连接、数据库游标释放锁threading.Lock打印/记录日志确保一定执行UI 中隐藏 Loading 加载动画Python 异常层级体系了解异常继承关系才能正确使用 except 捕获Python 异常继承自 BaseException → Exception捕获父类会同时捕获所有子类异常异常类触发条件常见示例BaseException所有异常的根类不建议直接捕获Exception所有标准异常的基类兜底时可捕获ValueError值不合法int(abc)TypeError类型不匹配a 1IndexError列表索引越界lst[100]KeyError字典键不存在d[x]AttributeError属性/方法不存在None.upper()FileNotFoundError文件不存在OSError 子类open(x.txt)ZeroDivisionError除数为零ArithmeticError子类1/0ImportError导入失败import xyzStopIteration迭代器耗尽next(iter([]))KeyboardInterrupt用户按 CtrlCBaseException 直接子类自定义异常类继承 Exception为业务逻辑创建语义明确的专属异常自定义异常继承自 Exception可按业务层级建立异常树如 AppError → NetworkError / DatabaseErrorcustom_exception.py# 定义自定义异常 class AppError(Exception): 应用层基础异常 def __init__(self, message, codeNone): super().__init__(message) self.code code def __str__(self): return f[错误码 {self.code}] {super().__str__()} class NetworkError(AppError): 网络请求相关异常 pass class DatabaseError(AppError): 数据库操作相关异常 def __init__(self, message, tableNone): super().__init__(message, code500) self.table table # 使用自定义异常 def connect_db(host): if not host: raise DatabaseError(数据库主机地址不能为空, tableusers) print(f✅ 连接到 {host}) try: connect_db() except DatabaseError as e: print(f数据库错误{e}) print(f问题表{e.table}) except AppError as e: # 父类兜底 print(f应用错误{e}) # 输出 # 数据库错误[错误码 500] 数据库主机地址不能为空 # 问题表users⭐最佳实践与注意事项写出健壮、可维护的异常处理代码左侧Bad捕获过宽、吞掉错误右侧Good精确捕获、记录日志、合理处理❌反面案例 — 不要这样写不要用裸except:捕获所有异常会连 KeyboardInterrupt 也捕获不要捕获异常后什么都不做异常静默是 Bug 的温床不要用异常控制正常程序流。best_practice.pyimport logging logging.basicConfig(levellogging.ERROR, format%(asctime)s - %(message)s) # ❌ 反面示例 1捕获所有异常后静默 try: result risky_operation() except: # 危险裸 except 连 CtrlC 都拦截 pass # 危险异常被吞掉出问题根本不知道 # ✅ 正确示例 1精确捕获 日志记录 try: result risky_operation() except ValueError as e: logging.error(f数值错误{e}) # 记录日志 result None # 给出安全默认值 # ✅ 正确示例 2捕获后重新抛出re-raise def process_data(data): try: return int(data) * 2 except ValueError as e: logging.error(f数据格式异常原始数据{data}) raise # 重新抛出让上层决定如何处理 # ✅ 正确示例 3异常链保留原始异常信息 def load_config(path): try: with open(path) as f: return f.read() except FileNotFoundError as e: raise RuntimeError(f配置文件加载失败{path}) from e # from e 保留原始异常调试更友好✅应该这样做捕获最精确的异常类型使用 logging 记录异常信息为用户提供有意义的错误提示使用raise重新抛出不能处理的异常用with语句代替 try/finally 管理资源❌避免这样做裸except:捕获一切捕获后只写pass吞掉异常在 except 里做复杂逻辑导致新异常捕获 BaseException除非顶层入口用异常来做正常流程控制综合实战案例模拟真实场景用户输入处理 文件读写 网络请求案例一安全的用户输入处理case1_input.pydef get_valid_age(): 循环获取合法年龄直到用户输入正确 while True: try: age_str input(请输入您的年龄) age int(age_str) # 可能 ValueError if age 0 or age 150: raise ValueError(年龄必须在 0-150 之间) except ValueError as e: print(f⚠️ 输入无效{e}请重新输入) else: print(f✅ 年龄 {age} 已记录) return age # 成功则返回 age get_valid_age() print(f您的年龄是{age})案例二带重试机制的网络请求case2_network.pyimport urllib.request import time def fetch_url(url, max_retries3, timeout5): 带重试机制的 HTTP 请求 for attempt in range(1, max_retries 1): try: print(f 第 {attempt} 次尝试请求 {url}) with urllib.request.urlopen(url, timeouttimeout) as resp: data resp.read().decode(utf-8) print(f✅ 请求成功响应长度{len(data)} 字符) return data except urllib.error.URLError as e: print(f❌ 网络错误第 {attempt} 次{e.reason}) if attempt max_retries: print(f 等待 {attempt} 秒后重试...) time.sleep(attempt) # 指数退避 print(❌ 全部重试失败返回 None) return None result fetch_url(https://example.com)案例三JSON 配置文件读取case3_config.pyimport json import os def load_config(config_path: str) - dict: 安全读取 JSON 配置文件 try: with open(config_path, r, encodingutf-8) as f: config json.load(f) # 校验必须的字段 required [host, port, database] missing [k for k in required if k not in config] if missing: raise KeyError(f配置缺少必要字段{missing}) return config except FileNotFoundError: print(f❌ 配置文件不存在{config_path}) return {} except json.JSONDecodeError as e: print(f❌ JSON 格式错误第 {e.lineno} 行 - {e.msg}) return {} except KeyError as e: print(f❌ 配置校验失败{e}) return {} except Exception as e: print(f❌ 未知错误{type(e).__name__}: {e}) return {} config load_config(config.json) print(config)练习题动手写代码检验学习成果️ 动手练习练习 1 — 基础编写一个函数safe_divide(a, b)捕获除零异常和类型异常正常时返回结果异常时返回 None 并打印错误原因。练习 2 — 进阶编写read_numbers(filename)读取文件中每行的数字遇到非数字行跳过捕获 ValueError文件不存在时返回空列表最后无论如何都打印读取完成。练习 3 — 自定义异常创建AgeError自定义异常类编写validate_age(age)当 age 不是整数抛出 TypeError当 age 不在 0-150 之间抛出 AgeError并附带错误码。练习 4 — 综合模拟简单计算器循环接收用户输入的算式如 10 / 2解析并计算使用完整的 try/except/else/finally 处理各种可能的异常输入 quit 时退出循环。快速记忆口诀try放危险代码except捕捉异常else成功才走finally总收尾。捕获要精确日志要记录不能吞异常需要就重抛