Skip to content

29|异常处理

程序运行过程中会出现各种意外情况:文件不存在、网络超时、类型不匹配、除以零。这些不叫 bug,叫异常(exception)——程序预期外但可能发生的事件。异常处理让程序遇到问题时优雅降级,而不是直接崩溃。

一、try-except

python
try:
    result = 10 / 0
except ZeroDivisionError:
    result = None
    print("除数不能为零")

print(result)   # None

try 块里的代码正常执行。如果抛出异常,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("无论是否异常,都会执行")
  • elsetry 块没有异常时执行
  • 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 exc

raise ... 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 始终执行;自定义异常继承 Exceptionraise ... from exc 保留异常链;异常不是流程控制工具,正常分支用 if