Python 进阶详解:异常处理(Exception Handling)—— 让程序更健壮、更安全

一、引言:什么是异常?

在程序运行过程中,可能会遇到各种意外情况,例如:

  • ❌ 用户输入了无效的数据
  • ❌ 尝试打开一个不存在的文件
  • ❌ 除以零
  • ❌ 网络连接中断
  • ❌ 访问列表越界

这些情况会导致程序中断执行并抛出异常(Exception)。如果不加以处理,程序会崩溃并显示错误信息。

✅ 异常处理的目标:让程序在出错时能够优雅降级,而不是直接崩溃,提升程序的健壮性和用户体验。

Python 提供了强大的异常处理机制,允许我们捕获和处理错误,确保程序的稳定运行。


二、常见的内置异常类型

异常类型

描述

示例

Exception

所有内置异常的基类

通用捕获

ValueError

值不合适(类型正确但值非法)

int("abc")

TypeError

类型错误

len(123)

IndexError

序列索引超出范围

lst[100](只有3个元素)

KeyError

字典中不存在指定的键

dct['missing_key']

FileNotFoundError

文件未找到

open("missing.txt")

ZeroDivisionError

除以零

10 / 0

IOError

输入/输出操作失败

读写文件失败

ImportError

模块导入失败

import non_existent_module

AttributeError

对象没有该属性

obj.nonexistent_method()

💡 查看所有内置异常:

help('built-in')

三、基本语法:try-except 结构

1. 最简单的异常处理(不推荐)

try:
    result = 10 / 0
except:
    print("发生了错误!")

⚠️ 不推荐:捕获所有异常而不指定具体类型,不利于调试和错误定位。


2. 捕获特定异常(推荐)

try:
    num = int(input("请输入一个数字:"))
    result = 10 / num
except ValueError:
    print("输入的不是有效数字!")
except ZeroDivisionError:
    print("不能除以零!")

✅ 推荐:精确捕获你预期的异常类型。


3. 捕获多个异常

try:
    value = int("abc")
except (ValueError, TypeError):
    print("转换失败:输入的值无法转换为整数")

✅ 使用元组 (A, B) 可以同时捕获多种异常。


4. 获取异常信息

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"发生异常:{e}")  # 输出:division by zero

✅ 使用 as e 可以获取异常实例,便于日志记录或进一步处理。


四、完整的异常处理结构:try-except-else-finally

1. else 子句

try 块中没有发生异常时执行。

try:
    num = int(input("请输入一个数字:"))
except ValueError:
    print("输入无效!")
else:
    print(f"你输入的数字是:{num}")
    # 只有在没有异常时才会执行

else 用于放置“成功路径”的代码,避免意外捕获本不该处理的异常。


2. finally 子句

无论是否发生异常,都会执行,常用于清理资源(如关闭文件、释放锁等)。

file = None
try:
    file = open("test.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("文件未找到")
finally:
    if file:
        file.close()  # 确保文件被关闭
        print("文件已关闭")

✅ 保证资源释放,防止资源泄漏。


✅ 最佳实践:使用 with 语句自动管理资源

# 推荐方式:自动管理文件资源
try:
    with open("test.txt", "r") as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print("文件未找到")
# 文件会自动关闭,无需 finally

with 语句利用上下文管理器(Context Manager)自动调用 __enter____exit__,更安全、简洁。


五、手动抛出异常:raise

你可以使用 raise 语句主动抛出异常,用于验证输入、业务规则等。

1. 抛出内置异常

age = -5
if age < 0:
    raise ValueError("年龄不能为负数")

2. 抛出自定义异常

class InvalidAgeError(Exception):
    """自定义异常:无效年龄"""
    pass

age = 150
if age > 120:
    raise InvalidAgeError("年龄不能超过120岁")

六、自定义异常

通过继承 Exception 类,可以创建语义清晰、信息丰富的自定义异常。

示例:银行取款余额不足异常

class InsufficientFundsError(Exception):
    def __init__(self, balance, amount):
        self.balance = balance
        self.amount = amount
        super().__init__(f"余额不足:当前余额 {balance},请求取款 {amount}")

# 使用示例
def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(balance, amount)
    return balance - amount

# 捕获并处理
try:
    new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
    print(e)  # 输出:余额不足:当前余额 100,请求取款 150
    print(f"差额:{e.amount - e.balance}")  # 50

✅ 自定义异常可以携带上下文信息,便于调试和业务处理。


七、异常处理的最佳实践

1. 只捕获你知道如何处理的异常

# ❌ 错误做法:静默忽略所有错误
try:
    risky_operation()
except:
    pass

# ✅ 正确做法:只处理特定异常
try:
    process_data()
except ValueError as e:
    print(f"数据格式错误:{e}")
except FileNotFoundError:
    print("文件不存在,请检查路径")

2. 提供有意义的错误信息

try:
    user = users[user_id]
except KeyError as e:
    print(f"用户不存在:ID {user_id} 未找到")

✅ 帮助用户或开发者快速定位问题。


3. 使用 logging 替代 print(生产环境)

import logging

try:
    connect_to_database()
except ConnectionError as e:
    logging.error(f"数据库连接失败:{e}", exc_info=True)

logging 支持日志级别、格式化、文件输出等,更适合生产环境。


4. 避免在 finally 中使用 return

def bad_example():
    try:
        raise ValueError("oops")
    finally:
        return "done"  # ❌ 会掩盖上面的异常!

print(bad_example())  # 输出:done,但原始异常丢失!

⚠️ finally 中的 return 会覆盖tryexcept 中的返回值或异常。


八、综合示例:一个健壮的计算器

def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("错误:不能除以零!")
        return None
    except TypeError:
        print("错误:请输入有效的数字!")
        return None
    else:
        print(f"计算成功:{a} / {b} = {result}")
        return result
    finally:
        print("除法操作完成")

# 测试
print(safe_divide(10, 2))   # 5.0
print(safe_divide(10, 0))   # None
print(safe_divide(10, "a")) # None

✅ 包含完整异常处理流程:捕获、处理、成功路径、清理。


九、总结:异常处理的核心要点

结构

作用

是否必需

try

包含可能出错的代码

✅ 必需

except

捕获并处理异常

⚠️ 建议使用

else

try成功时执行

❌ 可选

finally

无论是否异常都执行(清理)

❌ 可选

raise

主动抛出异常

❌ 按需使用


十、动手练习

1. 编写一个函数,读取用户输入的两个数字并计算它们的商

def input_and_divide():
    try:
        a = float(input("请输入被除数:"))
        b = float(input("请输入除数:"))
        result = a / b
    except ValueError:
        print("请输入有效的数字!")
    except ZeroDivisionError:
        print("除数不能为零!")
    else:
        print(f"结果是:{result}")
    finally:
        print("计算完成")

input_and_divide()

2. 创建自定义异常 NegativeNumberError

class NegativeNumberError(Exception):
    def __init__(self, value):
        self.value = value
        super().__init__(f"不允许负数:{value}")

def process_positive_number(num):
    if num < 0:
        raise NegativeNumberError(num)
    return num * 2

# 测试
try:
    print(process_positive_number(5))   # 10
    print(process_positive_number(-3))  # 抛出异常
except NegativeNumberError as e:
    print(e)

3. 使用 try-finally 确保资源关闭(模拟数据库连接)

class DatabaseConnection:
    def __init__(self, db_name):
        self.db_name = db_name
        self.connected = False

    def connect(self):
        print(f"连接到数据库 {self.db_name}...")
        self.connected = True

    def close(self):
        if self.connected:
            print(f"关闭数据库连接 {self.db_name}")
            self.connected = False

# 模拟使用
conn = DatabaseConnection("mydb")

try:
    conn.connect()
    # 模拟操作
    print("执行数据库查询...")
    # raise Exception("查询失败")  # 测试异常
except Exception as e:
    print(f"数据库操作失败:{e}")
finally:
    conn.close()  # 确保连接关闭

🚀 学习建议

  1. 多练习:编写包含异常处理的函数,模拟各种错误场景。
  2. 理解异常层次结构BaseException → Exception → ...,便于精确捕获。
  3. 设计良好的异常策略:根据业务需求决定是“失败重试”、“降级处理”还是“直接报错”。
  4. 进阶学习
    • 上下文管理器(__enter__, __exit__
    • 异常链(raise ... from ...
    • 断言(assert)与调试

🎯 记住良好的异常处理是专业代码的标志。它不仅防止程序崩溃,还能提供清晰的反馈,提升系统可靠性。

祝你写出健壮、可靠的 Python 程序!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值