当前位置: 首页 > news >正文

Try和expect的正确使用方式

💥

什么是异常(Exception)

程序运行时发生的错误事件,若不处理会导致程序崩溃

⚠️异常 vs 错误

Python 中有两类问题:语法错误(SyntaxError)在代码运行前就被发现;异常(Exception)在运行时才会触发。try/except 只能处理运行时异常,语法错误必须手动修复代码。

常见异常类型一览

ValueError

值类型正确但内容不合法

int("abc")

TypeError

操作或函数作用于错误类型

"5" + 5

IndexError

序列索引超出范围

[1,2,3][10]

KeyError

字典中键不存在

{}["key"]

ZeroDivisionError

除数为零

10 / 0

FileNotFoundError

文件或目录不存在

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 → 最终无论如何都执行 finally

1

🟢 try 块开始执行

Python 逐行执行 try 块中的代码,一旦某行抛出异常,立即跳转到对应的 except 块,try 块剩余代码不再执行

2

🔴 异常匹配 except

Python 从上到下依次检查 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', encoding='utf-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', encoding='utf-8') as f: return f.read() # with 自动处理 close() except FileNotFoundError: print("❌ 文件不存在") return None

🟡

else 的使用场景

  • 将"成功后的逻辑"与 try 分离,代码更清晰
  • 避免 try 块过长、意外捕获 else 中的异常
  • 只在真正没有异常时才执行数据库写入等操作

🟣

finally 的使用场景

  • 关闭文件句柄、网络连接、数据库游标
  • 释放锁(threading.Lock)
  • 打印/记录日志(确保一定执行)
  • UI 中隐藏 Loading 加载动画

🌳

Python 异常层级体系

了解异常继承关系,才能正确使用 except 捕获

Python 异常继承自 BaseException → Exception,捕获父类会同时捕获所有子类异常

异常类触发条件常见示例
BaseException所有异常的根类不建议直接捕获
Exception所有标准异常的基类兜底时可捕获
ValueError值不合法int("abc")
TypeError类型不匹配"a" + 1
IndexError列表索引越界lst[100]
KeyError字典键不存在d["x"]
AttributeError属性/方法不存在None.upper()
FileNotFoundError文件不存在(OSError 子类)open("x.txt")
ZeroDivisionError除数为零(ArithmeticError子类)1/0
ImportError导入失败import xyz
StopIteration迭代器耗尽next(iter([]))
KeyboardInterrupt用户按 Ctrl+CBaseException 直接子类

🏗

自定义异常类

继承 Exception,为业务逻辑创建语义明确的专属异常

自定义异常继承自 Exception,可按业务层级建立异常树,如 AppError → NetworkError / DatabaseError

custom_exception.py

# ===== 定义自定义异常 ===== class AppError(Exception): """应用层基础异常""" def __init__(self, message, code=None): 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, table=None): super().__init__(message, code=500) self.table = table # ===== 使用自定义异常 ===== def connect_db(host): if not host: raise DatabaseError("数据库主机地址不能为空", table="users") 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.py

import logging logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(message)s') # ❌ 反面示例 1:捕获所有异常后静默 try: result = risky_operation() except: # 危险!裸 except 连 Ctrl+C 都拦截 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.py

def 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.py

import urllib.request import time def fetch_url(url, max_retries=3, timeout=5): """带重试机制的 HTTP 请求""" for attempt in range(1, max_retries + 1): try: print(f"🔄 第 {attempt} 次尝试请求 {url}") with urllib.request.urlopen(url, timeout=timeout) 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.py

import json import os def load_config(config_path: str) -> dict: """安全读取 JSON 配置文件""" try: with open(config_path, 'r', encoding='utf-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总收尾。捕获要精确,日志要记录,不能吞异常,需要就重抛!

http://www.jsqmd.com/news/881042/

相关文章:

  • 连锁董事网络指标数据(2001-2024)
  • 2026电工杯数学建模竞赛A题论文、代码、数据
  • 数据结构:线性表和顺序表
  • 2026槽式电缆桥架优质推荐指南:网格电缆桥架、铝合金走线架、不锈钢电缆桥架、北京电缆桥架厂家、托盘式电缆桥架选择指南 - 优质品牌商家
  • Claude Code 在安装vscode插件时遇到的问题。
  • 告别图形界面!5个CUPS命令行技巧,让你在Linux终端高效管理打印机
  • 2026微型舵机优质推荐榜:小型舵机/尾翼用方扁舵机/工业舵机/德晟舵机/数字舵机/无人机舵机/无刷舵机/最小的舵机/选择指南 - 优质品牌商家
  • 2026电工杯数学建模竞赛A题论文、代码、数据(改进)
  • # 网页设计学习感悟
  • 朝晖玻璃钢:玻璃钢保温水箱/玻璃钢消防水箱/玻璃钢罐化粪池/碳钢水箱/立式不锈钢水箱/组合式玻璃钢水箱/雨水一体化提升泵站/选择指南 - 优质品牌商家
  • 别再手动下载DLL了!用Windows自带工具SFC/SCANNOW一键修复kernel32.dll错误
  • 别再让系统‘无家可归’:给已用满空间的Win10 SSD无损创建EFI引导分区指南
  • 2026年紫外线杀菌除藻灯优质厂家深度解析:聚焦技术、产能与服务三角 - 2026年企业推荐榜
  • Titanic数据集分析避坑指南:新手常犯的3个错误及如何修正
  • ubuntu2026.04部署k8s1.36版本的傻瓜式教程(注:运行时为docker,网络插件为calico)
  • 一文讲清楚规则、Skill、MCP
  • 2026泛塞封密封圈优质品牌推荐:聚四氟乙烯密封圈/铁氟龙密封圈/高分子材料密封圈/O型圈/PEEK密封圈/PU密封圈/选择指南 - 优质品牌商家
  • 别再让Ubuntu卡成PPT!手把手教你用swapfile把交换空间从1G扩容到64G(附权限修复)
  • 【iOS】底层原理:理解dyld
  • 告别虚拟机!手把手教你用U盘给新电脑装Win11+统信UOS 1060双系统(保姆级分区教程)
  • Win10开机WiFi列表全空?先别慌,按这个‘服务状态排查流程图’走一遍
  • 2026靠谱仪器推荐:Trim200离子束刻蚀机、Essent Optics分光光度计、LINZA分光光度计、LensCheck MTF传函仪选择指南 - 优质品牌商家
  • 告别下载量低迷,5套实操方法打通用户增长
  • MacBook新手福音:用Final Cut Pro 10.6.5搞定你的第一门视频课(附保姆级设置与导出指南)
  • 2026年知名的大豆定量包装机/饲料定量包装机厂家哪家好 - 行业平台推荐
  • 从零开始手把手教你用Python和XFLR5估算小型固定翼无人机的升力系数(附代码)
  • 2026北京搬家公司优质推荐指南:北京公司搬家公司/北京收纳整理公司/北京日式搬家公司/北京本地搬家/北京企业搬家/选择指南 - 优质品牌商家
  • 【程序源代码】答题微信小程序(含源码)
  • Cocos Creator 3.x 实战:用 BoxCollider 和 CircleCollider 快速搞定一个2D平台跳跃游戏的碰撞检测
  • 避坑指南:在openEuler 22.03上配置vsftpd虚拟用户,解决gdbmtool替代db_load的认证问题