Python基础学习-11(异常处理)
异常处理
1. 知识点
核心概念
Python 中所有异常都继承自 BaseException(常用的是 Exception)。当代码出错时 Python “抛出”(raise)一个异常对象,你可以用 try 块"捕获"(catch)它。
关键表格对比
| 关键字 | 作用 | 执行时机 | 是否必须 |
|---|---|---|---|
try | 包裹可能出错的代码 | 始终执行 | 是 |
except | 捕获并处理特定异常 | 仅当 try 块中抛出匹配异常时 | 是(最少一个) |
else | 无异常时执行的代码 | try 块没有抛出异常时 | 否 |
finally | 清理收尾工作 | 无论如何都会执行(哪怕有 return) | 否 |
raise | 主动抛出异常 | 调用时立即抛出 | 否 |
try/except 三种写法对比
| 写法 | 示例 | 适用场景 | 风险 |
|---|---|---|---|
| 捕获具体异常 | except ValueError: | 知道会出什么错 | 低,精准处理 |
| 捕获多个异常 | except (ValueError, KeyError): | 两类错处理方式相同 | 低 |
| 裸捕获 | except: 或 except Exception: | 兜底保护 | 中高:可能掩盖意料之外的 bug |
捕获越具体越好,裸
except:只在最外层兜底使用。
常见异常速查表
| 异常类 | 触发场景 | 示例 |
|---|---|---|
ZeroDivisionError | 除以零 | 10 / 0 |
ValueError | 类型对但值不对 | int("abc") |
TypeError | 类型不匹配 | "hello" + 5 |
KeyError | 字典键不存在 | d["no_such_key"] |
IndexError | 列表索引越界 | lst[999] |
FileNotFoundError | 文件不存在 | open("不存在的文件.txt") |
AttributeError | 属性或方法不存在 | "abc".non_exist() |
ImportError | 模块导入失败 | import 不存在的模块 |
2.代码示例:
示例一:基础try/except——除法保护
def safe_divide(a,b):
"""安全排除法"""
try:
result = a / b
except ZeroDivisionError:
print("错误,除数不能为0")
return None
except TypeError:
print("错误,请输入数字类型")
return None
else:
print(f"计算成功:{a} / {b} = {result}")
if __name__ == '__main__':
#测试三种情况
safe_divide(10,2)
safe_divide(10,0)
safe_divide(10,"a")
代码流程说明:
try块逐行执行,一旦某行出错立刻跳到匹配的except;如果全部顺利执行完,进入else。
示例二:finally — 无论如何都要执行的清理
def read_file_with_cleanup(path):
"""演示 finally:无论成功失败都要关闭文件"""
f = None # 先置空,确保 finally 中可判断
try:
f = open(path, "r", encoding="utf-8") # 打开文件
content = f.read() # 读取内容
print(f"读取成功,文件长度:{len(content)} 字符")
return content # 注意:即使 return,finally 也会执行!
except FileNotFoundError:
print(f"文件不存在:{path}")
except PermissionError:
print(f"没有权限读取:{path}")
finally:
if f: # 如果文件成功打开了
f.close() # 确保关闭
print("文件已关闭(finally 执行)")
if __name__ == '__main__':
read_file_with_cleanup("不存在的文件.txt")
print("-"*30)
result = read_file_with_cleanup(__file__)#读取自身
print(f"返回值:{result[:50]}")
关键点:即使 try 里有 return,finally 也会在函数真正返回之前执行——这是 Python 的保证。
示例3:主动抛出异常+自定义异常
class AgeError(Exception):
"""自定义异常,年龄不合法"""
def __int__(self,age,message="年龄必须在0—150之间"):
self.age=age
self.message=f"{message}(你输入了{age})"
super().__int__(self.message)
def set_age(age):
"""设置年龄,不合法时抛出AgeError"""
if not isinstance(age,int):
raise TypeError(f"年龄必须是整数,不能是{type(age).__name__}")
if age < 0 or age >150:
raise AgeError
print(f"年龄设置为{age}岁")
return age
test_values = [25,-5,"三十岁"]
for val in test_values:
try:
set_age(val)
except AgeError as e: # 捕获自定义异常,打印详细信息
print(f"[自定义异常]{e}")
except TypeError as e:
print(f"[异常类型]{e}")
print("--------------")
设计思路:AgeError 继承 Exception,带上了出错的 age 值和描述信息,让调用方能拿到丰富上下文。
示例4:异常链 — raise…from
def load_config(path):
"""加载配置文件,底层异常通过raise...from传递"""
import json
try:
with open(path,"r") as f:
return json.load(f)
except FileNotFoundError as e:
#把底层异常包装成更高级的异常,保留原始原因
raise RuntimeError(f"配置文件{path}不存在") from e
except json.JSONDecodeError as e:
raise RuntimeError(f"配置文件{path} JSON格式错误") from e
try:
load_config("不存在的配置.json")
except RuntimeError as e:
print(f"捕获到:{e}")
print(f"原始原因:{e.__cause__}")
raise...from 的作用:保留原始异常的调用栈信息,方便调试时追溯根因。
补充知识点
-
异常不是控制流:不要用
try/except代替if检查。比如用if key in dict而不是try: dict[key] except KeyError——前者更清晰且性能更好。 -
except Exceptionvs 裸except:裸except会捕获KeyboardInterrupt和SystemExit,可能导致你的程序连 Ctrl+C 都无法终止。永远优先用except Exception。 -
异常链的价值:
raise NewError(...) from original_error保留完整的因果链,比简单raise NewError(...)更容易定位根因。 -
try/finally模式:管理资源(文件、网络连接、锁)时,优先用with语句(自动调用__exit__),而不是手写try/finally。with本身就是对try/finally的优雅封装。
更多推荐
所有评论(0)