Python: 异常处理

程序运行中总会出现意外情况——文件不存在、网络断开、用户输入了非法数据……如果不处理这些"异常",程序就会崩溃退出。异常处理让你的程序能从错误中恢复,给用户友好的提示,而不是留下一堆看不懂的错误信息。


1. 什么是异常

异常(Exception)就是程序运行时发生的错误。遇到异常而没处理,程序就会终止:

PYTHON
# 这段代码会崩溃
# print(10 / 0)               # ZeroDivisionError: division by zero
# int("abc")                  # ValueError: invalid literal for int()
# open("不存在的文件.txt")    # FileNotFoundError

try-except 可以优雅地处理这些错误:

▶ 示例:try-except 处理除零和输入错误

PYTHON
try:
    number = int(input("请输入一个数字:"))
    result = 10 / number
    print(f"10 ÷ {number} = {result}")
except ValueError:
    print("输入的不是有效数字!")
except ZeroDivisionError:
    print("不能除以零!")
▶ 试一试

运行示例:

TEXT 📖 仅展示
请输入一个数字:abc
输入的不是有效数字!
💡 异常处理的哲学: 不是用 try 去"预测所有可能的错误",而是"先把事情做了,遇到问题再处理"。Python 社区的说:"It's easier to ask for forgiveness than permission"(EAFP——请求原谅比请求许可更容易)。



2. try-except 的完整结构

▶ 示例:try-except-else-finally 完整结构

PYTHON
try:
    # 可能出错的代码
    file = open("data.txt", "r", encoding="utf-8")
    content = file.read()
    number = int(content)
    print(f"数字是:{number}")
except FileNotFoundError:
    print("文件不存在!")
except ValueError:
    print("文件内容不是有效的数字!")
else:
    # 没有异常时执行
    print("读取并转换成功!")
finally:
    # 不管有没有异常都执行
    print("清理工作:关闭文件")
    try:
        file.close()
    except NameError:
        pass
▶ 试一试

输出:

TEXT 📖 仅展示
文件不存在!
文件内容不是有效的数字!
读取并转换成功!
清理工作:关闭文件
部分 什么时候执行 用途
try 总是 放可能出错的代码
except 发生指定异常时 处理错误
else 没有异常时 成功后的逻辑
finally 总是(不管有没有异常) 清理资源(关闭文件/释放连接)
💡 实际开发中,else 用得比较少,finally 用得很多。 with 语句(上下文管理器)能自动处理资源释放,很多时候可以替代 finally



3. 常见异常类型

PYTHON
# ZeroDivisionError —— 除零
# int("abc")                # ValueError —— 值错误
# [1, 2][5]                 # IndexError —— 索引越界
# {"a": 1}["b"]             # KeyError —— 键不存在
# open("x.txt")             # FileNotFoundError —— 文件不存在
# 10 + "hello"              # TypeError —— 类型错误
# import nonexistent        # ModuleNotFoundError —— 模块不存在

(1) 捕获所有异常(但不推荐)

▶ 示例:捕获异常的正确与错误方式

PYTHON
try:
    # 一些代码
    pass
except:                     # ❌ 捕获所有异常
    print("出错了")

try:
    pass
except Exception as e:      # ✅ 捕获已知的所有异常
    print(f"出错了:{e}")
▶ 试一试

输出:

TEXT 📖 仅展示
出错了
⚠️ 裸 except: 不推荐—— 它会捕获 KeyboardInterrupt(Ctrl+C)和 SystemExit 等系统异常,导致程序无法正常终止。用 except Exception as e 更合适。最好的做法是只捕获你预期会发生的具体异常类型



4. raise:主动抛出异常

有时候你需要主动告诉调用方"出错了",用 raise

▶ 示例:raise 抛出异常

PYTHON
def divide(a, b):
    if b == 0:
        raise ValueError("除数不能为0!")
    return a / b

try:
    result = divide(10, 0)
except ValueError as e:
    print(f"错误:{e}")     # 错误:除数不能为0!
▶ 试一试

输出:

TEXT 📖 仅展示
# 函数定义成功

(1) 密码强度检查示例

▶ 示例:raise 检查密码强度

PYTHON
def validate_password(password):
    """验证密码强度,不符合条件则抛出异常"""
    if len(password) < 6:
        raise ValueError("密码长度至少6位")
    if not any(c.isdigit() for c in password):
        raise ValueError("密码必须包含数字")
    if not any(c.isalpha() for c in password):
        raise ValueError("密码必须包含字母")
    return True

try:
    validate_password("123")
except ValueError as e:
    print(f"密码不合格:{e}")
▶ 试一试

输出:

TEXT 📖 仅展示
# 函数定义成功
💡 什么时候用 raise 当函数遇到了"不应该发生的"情况时——参数值非法、前置条件不满足、依赖的资源不可用。抛出异常让调用方决定怎么处理,而不是在函数内部偷偷吞掉错误。



5. 自定义异常类

你可以创建自己的异常类型,让错误信息更精确:

▶ 示例:自定义异常与银行取款

PYTHON
class BalanceError(Exception):
    """余额不足异常"""
    pass

class AccountLockedError(Exception):
    """账户锁定异常"""
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise BalanceError(f"余额不足!需要{amount},当前余额{balance}")
    if balance < 0:
        raise AccountLockedError("账户已锁定")
    return balance - amount

try:
    withdraw(100, 200)
except BalanceError as e:
    print(f"交易失败:{e}")
except AccountLockedError as e:
    print(f"交易失败:{e}")
▶ 试一试

输出:

TEXT 📖 仅展示
# 函数定义成功
💡 自定义异常让你的代码更清晰—— 调用方可以精确处理不同类型的错误。直接继承 Exception 类即可,通常只需要写一个类名,不需要额外的方法。



6. logging:专业的错误记录

print() 输出错误信息在简单脚本里还行,但在正式项目中,应该用 logging 模块:

▶ 示例:logging 配置与使用

PYTHON
import logging

# 基本配置
logging.basicConfig(
    level=logging.INFO,          # 日志级别
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("app.log", encoding="utf-8"),  # 写入文件
        logging.StreamHandler()                            # 同时输出到控制台
    ]
)

# 不同级别的日志
logging.debug("调试信息")        # 调试用,一般不输出
logging.info("程序启动成功")     # 一般信息
logging.warning("磁盘使用率85%")  # 警告
logging.error("数据库连接失败")   # 错误
logging.critical("系统崩溃!")    # 致命错误
▶ 试一试

运行结果(同时写入文件和控制台):

TEXT 📖 仅展示
2026-06-23 16:00:00,123 [INFO] 程序启动成功
2026-06-23 16:00:00,124 [WARNING] 磁盘使用率85%
2026-06-23 16:00:00,124 [ERROR] 数据库连接失败
💡 用 logging 代替 print() 的好处: ① 可以同时输出到控制台和文件。② 有级别控制(开发时全开,上线后只显示 WARNING 以上)。③ 自动带时间戳。④ 不需要在发布前删掉一堆 print()



7. 常见应用场景


❓ 常见问题

Q except Exception as e 中的 as e 是什么意思?
A as e 把捕获到的异常对象赋值给变量 e,这样你就可以查看异常的具体信息——print(e) 显示错误描述,type(e).__name__ 显示异常类型名。不加 as e 也能捕获异常,但看不到详细错误信息。
Q 什么时候应该用异常,什么时候应该用 if 判断?
A 可以用 if 预判的错误就用 if(比如检查除数为零)。无法预判的错误用 try-except(比如网络请求、文件操作)。Python 社区的 EAFP 风格倾向用 try——不过这是一个审美问题,两种方式各有适用场景。
Q finally 中的代码一定会执行吗?
A 几乎总是——即使 try 块中有 returnbreakcontinuefinally 仍然会执行。唯一的例外是程序被强制终止(如 os._exit() 或系统断电)。这使 finally 成为释放资源的绝佳场所。

📖 小节


📝 作业

  1. 基础题(难度⭐):写一个程序,让用户输入两个数字,计算它们的除法。用 try-except 处理 ValueError(输入不是数字)和 ZeroDivisionError(除数为零),分别给出友好的错误提示。

  2. 进阶题(难度⭐⭐):写一个函数 read_int_from_file(filename),读取文件中的第一个数字并返回。用 try-except 处理:文件不存在、文件内容不是有效数字、文件为空等错误情况。每种情况返回不同的错误提示。

  3. 挑战题(难度⭐⭐⭐):写一个"安全的计算器"程序。用户输入表达式(如 10 / 3),程序计算并输出结果。要求:① 用 try-except 处理除零、值错误、类型错误。② 用 logging 记录每次的计算操作(INFO)和错误(ERROR)到 calculator.log 文件。③ 支持 exit 退出。提示:用 eval() 计算表达式(注意安全问题,此练习场景可用)。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏