Appearance
29|异常处理
程序运行过程中会出现各种意外情况:文件不存在、网络超时、类型不匹配、除以零。这些不叫 bug,叫异常(exception)——程序预期外但可能发生的事件。异常处理让程序遇到问题时优雅降级,而不是直接崩溃。
一、try-except
python
try:
result = 10 / 0
except ZeroDivisionError:
result = None
print("除数不能为零")
print(result) # Nonetry 块里的代码正常执行。如果抛出异常,Python 跳到匹配的 except 块执行。
捕获多种异常:
python
try:
num = int(input("输入数字: "))
result = 100 / num
except ValueError:
print("输入的不是有效数字")
except ZeroDivisionError:
print("不能除以零")合并捕获:
python
try:
...
except (ValueError, TypeError):
print("数值类型错误")二、捕获异常对象
获取异常的具体信息:
python
try:
with open("missing.txt", "r") as f:
content = f.read()
except FileNotFoundError as exc:
print(f"文件未找到: {exc}")
# 输出:文件未找到: [Errno 2] No such file or directory: 'missing.txt'exc 是异常对象,包含错误信息。打印异常对象时,显示的是异常的描述文本。
三、捕获所有异常
用 Exception 捕获大部分异常:
python
try:
risky_operation()
except Exception as exc:
print(f"出错了: {exc}")except: 不带参数也能捕获所有异常,但不推荐——它连 KeyboardInterrupt(Ctrl+C)和 SystemExit 都捕获,让程序无法正常退出。
好的实践是:捕获具体的异常类型,不要 blanket catch。
四、else 和 finally
python
try:
f = open("data.txt", "r")
except FileNotFoundError:
print("文件不存在")
else:
content = f.read()
print(content)
f.close()
finally:
print("无论是否异常,都会执行")else:try块没有异常时执行finally:无论是否异常都执行,适合清理资源
else 的使用场景:把"没有异常时的后续操作"从 try 块里分离出来,让 try 块只包含可能抛异常的代码。
五、自定义异常
内置异常不够用時,可以定义自己的异常类:
python
class ValidationError(Exception):
"""数据校验失败。"""
pass
class ConfigError(Exception):
"""配置错误。"""
def __init__(self, message, filepath):
super().__init__(message)
self.filepath = filepath
def load_config(path):
if not path.exists():
raise ConfigError("配置文件不存在", path)
...自定义异常继承 Exception(或它的子类)。需要额外信息时,重写 __init__。
使用:
python
try:
config = load_config(Path("app.conf"))
except ConfigError as exc:
print(f"配置错误: {exc}, 文件: {exc.filepath}")六、异常链
捕获一个异常后,抛出另一个异常,同时保留原始异常信息:
python
try:
raw_data = fetch_from_api()
config = json.loads(raw_data)
except json.JSONDecodeError as exc:
raise ConfigError("API 返回的 JSON 格式错误") from excraise ... from exc 保留异常链。查看 traceback 时,能看到原始异常和重新抛出的异常之间的因果关系。
七、常见内置异常
| 异常 | 触发场景 |
|---|---|
ValueError | 类型正确但值不合适,如 int("abc") |
TypeError | 类型错误,如字符串和数字相加 |
KeyError | 字典中键不存在 |
IndexError | 索引超出范围 |
FileNotFoundError | 文件不存在 |
PermissionError | 权限不足 |
ZeroDivisionError | 除以零 |
AttributeError | 对象没有该属性 |
ImportError | 导入模块失败 |
RuntimeError | 通用运行时错误 |
八、何时使用异常
异常不是流程控制工具。正常的分支逻辑用 if:
python
# 不好:用异常控制正常流程
try:
value = mapping[key]
except KeyError:
value = default
# 好:用 get 方法
value = mapping.get(key, default)异常适合处理"不应该发生但可能发生的错误"——文件不存在、网络断开、配置格式错误等。
记忆锚点:try-except 捕获异常;捕获具体类型,不要 blanket catch;except Exception as exc 获取异常对象;else 在无异常时执行,finally 始终执行;自定义异常继承 Exception;raise ... from exc 保留异常链;异常不是流程控制工具,正常分支用 if。