⚠️ 17. 异常处理

本章概述

学习 Python 的异常处理机制,让程序更健壮。 预计学习时间:40 分钟


17.1 什么是异常

程序运行时出现的错误就是异常。

如果不处理异常,程序会直接崩溃停止。

常见异常

异常类型说明
ZeroDivisionError除以零
NameError变量未定义
TypeError类型错误
ValueError值错误
IndexError索引越界
KeyError字典key不存在
FileNotFoundError文件不存在
AttributeError属性不存在
SyntaxError语法错误

示例

# ZeroDivisionError
print(10 / 0)
 
# NameError
print(undefined_var)
 
# IndexError
lst = [1, 2, 3]
print(lst[10])
 
# KeyError
d = {"name": "Tom"}
print(d["age"])

17.2 try-except 捕获异常

基本格式

try:
    可能出错的代码
except 异常类型:
    出错后执行的代码

示例

try:
    result = 10 / 0
    print(result)
except ZeroDivisionError:
    print("除数不能为零!")
 
print("程序继续运行")  # 程序不会崩溃,继续执行

捕获多个异常

try:
    # 可能出错的代码
    x = int(input("请输入一个数:"))
    result = 10 / x
    print(result)
except ValueError:
    print("输入的不是数字!")
except ZeroDivisionError:
    print("除数不能为零!")

一个 except 捕获多种异常

try:
    # 可能出错的代码
    x = int(input("请输入一个数:"))
    result = 10 / x
    print(result)
except (ValueError, ZeroDivisionError):
    print("输入有误!")

捕获所有异常

try:
    # 可能出错的代码
    ...
except Exception as e:
    print(f"出错了:{e}")

慎用

尽量不要捕获所有异常,可能会隐藏真正的问题。 只捕获你预期的、知道怎么处理的异常。

获取异常信息

try:
    10 / 0
except ZeroDivisionError as e:
    print(f"异常信息:{e}")
    print(f"异常类型:{type(e)}")

17.3 else 子句

没有异常时执行 else 块:

try:
    result = 10 / 2
except ZeroDivisionError:
    print("除数不能为零!")
else:
    print(f"计算结果:{result}")  # 没有异常才执行

else 的作用

把「正常情况的代码」放到 else 里,逻辑更清晰。


17.4 finally 子句

无论有没有异常,finally 块都会执行:

try:
    f = open("test.txt", "r")
    content = f.read()
    print(content)
except FileNotFoundError:
    print("文件不存在!")
finally:
    f.close()  # 无论是否出错,都会关闭文件
    print("文件已关闭")

finally 的用途

  • 关闭文件
  • 关闭数据库连接
  • 释放资源
  • 清理工作

with 语句更方便

操作文件时,用 with 语句比 try-finally 更简洁,推荐用 with


17.5 完整的异常处理结构

try:
    # 可能出错的代码
    ...
except 异常类型1:
    # 处理异常类型1
    ...
except 异常类型2:
    # 处理异常类型2
    ...
except Exception as e:
    # 处理其他异常
    ...
else:
    # 没有异常时执行
    ...
finally:
    # 无论有没有异常都执行
    ...

17.6 抛出异常 raise

主动抛出异常。

基本用法

def divide(a, b):
    if b == 0:
        raise ValueError("除数不能为零!")  # 主动抛出异常
    return a / b
 
try:
    divide(10, 0)
except ValueError as e:
    print(e)

什么时候用 raise?

  • 参数不合法时
  • 业务逻辑出错时
  • 把底层异常包装成更有意义的异常

17.7 自定义异常

自己定义异常类型,继承 Exception

示例

class AgeError(Exception):
    """年龄异常"""
    pass
 
def set_age(age):
    if age < 0 or age > 150:
        raise AgeError(f"年龄不合法:{age}")
    print(f"设置年龄为:{age}")
 
try:
    set_age(200)
except AgeError as e:
    print(e)

带自定义信息的异常

class MyError(Exception):
    def __init__(self, message, code):
        super().__init__(message)
        self.code = code
 
try:
    raise MyError("出错了", 500)
except MyError as e:
    print(f"错误信息:{e}")
    print(f"错误代码:{e.code}")

17.8 异常处理最佳实践

1. 只捕获你能处理的异常

不要用裸 except

# ❌ 不好:捕获所有异常
try:
    ...
except:
    ...
 
# ✅ 好:只捕获特定异常
try:
    ...
except ValueError:
    ...

2. 异常信息要详细

# ❌ 不好:只说"出错了"
try:
    ...
except ValueError:
    print("出错了")
 
# ✅ 好:说明具体是什么错
try:
    ...
except ValueError as e:
    print(f"输入值不合法:{e}")

3. 不要用异常控制正常流程

注意

异常是用来处理「意外情况」的,不要用来做正常的流程控制。

# ❌ 不好:用异常判断key是否存在
try:
    value = d[key]
except KeyError:
    value = default
 
# ✅ 好:用get方法
value = d.get(key, default)

4. 资源清理用 finally 或 with

# 文件操作推荐用 with
with open("file.txt", "r") as f:
    content = f.read()

17.9 常见异常场景

1. 用户输入处理

while True:
    try:
        age = int(input("请输入年龄:"))
        if age < 0 or age > 150:
            raise ValueError("年龄不在合理范围")
        break
    except ValueError as e:
        print(f"输入无效:{e},请重新输入")
 
print(f"你的年龄是:{age}")

2. 文件操作

try:
    with open("data.txt", "r", encoding="utf-8") as f:
        data = f.read()
except FileNotFoundError:
    print("文件不存在,使用默认数据")
    data = "默认数据"
except PermissionError:
    print("没有权限读取文件")
except Exception as e:
    print(f"读取文件出错:{e}")

3. 字典取值

d = {"name": "Tom", "age": 18}
 
# 方式1:用get(推荐)
age = d.get("age", 0)
 
# 方式2:用异常(不推荐,性能差)
try:
    age = d["age"]
except KeyError:
    age = 0

🔗 相关章节


📝 我的笔记

在这里记录你的理解、疑问和练习代码

# 你的练习代码
 

✅ 本章检查清单

  • 理解什么是异常
  • 掌握 try-except 捕获异常
  • 会使用 else 和 finally 子句
  • 会用 raise 抛出异常
  • 了解自定义异常
  • 掌握异常处理最佳实践
  • 知道常见的异常类型