Python 异常处理:`try-except`、`else`、`finally` 与自定义异常

一、 Python 异常处理:try-exceptelsefinally 与自定义异常

在程序运行时,可能会遇到错误(如输入值错误、文件不存在、等)。Python 提供了异常处理机制,可以优雅地捕获这些错误,避免程序崩溃。本文将介绍如何使用 try-except 捕获异常、elsefinally 的用法,并讲解如何自定义异常


1. 使用 try-except 捕获异常

try-except 块用于捕获代码中的错误,并在出现错误时执行特定操作。

基本用法
try:
    num = int(input("请输入一个数字: "))  # 输入不是数字可能抛出 ValueError
    print(f"你输入的数字是: {num}")
except ValueError:
    print("输入无效,请输入一个有效的数字!")
  • try:尝试执行这段代码。
  • except:当出现指定的异常时执行这里的代码。
捕获多种异常
try:
	# 输入0或非数字,则会发生异常. 
    result = 10 / int(input("输入一个非零整数: "))
except ValueError:
    print("输入无效,请输入一个整数。")
except ZeroDivisionError:
    print("除数不能为 0!")

2. elsefinally 的用法

  • elsetry 代码块没有抛出异常时执行。
  • finally:无论是否出现异常,都执行的代码块(关闭和释放资源通常在这里进行.)。
示例:elsefinally
try:
    # with ... as f: 块结束自动关闭资源.
    with open("data.txt", "r") as f:
        content = f.read()
except FileNotFoundError:
    print("未找到该文件!")
else:
    print(f"文件读取成功:{content}")
finally:
    print("操作结束。")
  • else:如果 try 代码块没有抛出异常,就会执行 else 中的代码。
  • finally:无论文件是否存在,finally 中的代码都会执行。

3. 捕获所有异常

使用 Exception 可以捕获所有类型的异常,但应谨慎使用,避免掩盖潜在错误。

try:
    x = 10 / 0
except Exception as e:
    print(f"出现异常:{e}")

4. 自定义异常

有时候内置异常不够满足需求,可以自定义异常类,用于抛出特定场景下的错误。

自定义异常类
class AgeError(Exception): # 继承 Exception
    """自定义异常:年龄错误"""
    pass
使用自定义异常
def check_age(age):
    if age < 0:
        raise AgeError("年龄不能为负数!") # 抛出异常
    else:
        print(f"年龄是:{age}")

try:
    check_age(-5)
except AgeError as e:
    print(f"捕获到自定义异常:{e}")
  • raise:用于主动抛出异常。
  • 自定义异常类:继承自 Exception,并实现自己的功能。

5. 常见陷阱与最佳实践

  • 不要滥用捕获所有异常:避免使用 except Exception 捕获所有异常,这会让你忽略一些潜在的逻辑错误。
  • 在合适的位置使用 finally:确保资源(如文件、网络连接)在任何情况下都能正确关闭。
  • 自定义异常需具体:自定义异常时应尽量清晰描述错误类型,避免泛用。

二、Python 异常处理的高级用法

在掌握了基本的 try-except 语法后,Python 的异常处理还提供了更多高级特性,包括链式异常、上下文管理、重新抛出异常、自定义异常类的优化等。


1. 链式异常 (raise ... from ...)

Python 支持将一个异常与其根本原因关联起来,这叫链式异常。这在捕获某异常后,如果需要抛出另一个新的异常时非常有用。

示例:链式异常
try:
    result = 100 / 0
except ZeroDivisionError as e:
    raise ValueError("计算失败") from e  # 关联原始异常

解释

  • 这里捕获了 ZeroDivisionError,并抛出新的 ValueError
  • from e 关联了两个异常,便于追踪错误的根源

输出

    result = 100 / 0
             ~~~~^~~
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
 ......
ValueError: 计算失败

2. 重新抛出异常 (raise)

在某些情况下,我们希望捕获异常后执行某些操作,但仍然将原始异常继续抛出

示例:重新抛出异常
try:
    result = 10 / 0
except ZeroDivisionError:
    print("出现/0的异常, 但是希望继续抛出给后面处理")
    raise  # 重新抛出异常,让调用者处理

解释

  • raise 没有参数时,会重新抛出当前捕获的异常
  • 当前只进行log输出,再抛出原始异常让后续处理比较常用.

3. 自定义异常类的优化

我们可以创建更复杂的自定义异常,并支持自定义的错误信息上下文数据

示例:自定义异常类
class InvalidAgeError(Exception):
    """
    自定义异常:非法年龄.
    可以设定一些初始化时候参数.
    """
    def __init__(self, age, message="年龄不合法"):
        self.age = age
        self.message = message
        super().__init__(f"{message}: {age}")

# 使用自定义异常
def validate_age(age):
    if age < 0 or age > 150:
        raise InvalidAgeError(age) # 参数传入错误的年龄.

try:
    validate_age(200)
except InvalidAgeError as e:
    print(f"年龄无效异常: {e}")

解释

  • 自定义异常类可以设定包含一些信息,如非法的年龄值 age
  • 错误发生后的上下文更加清晰,方便排查。

4. 捕获并分类多个异常

在复杂的场景中,可能会出现多种不同类型的异常。你可以使用元组捕获多个异常类型。

示例:捕获多个异常
try:
    num = int(input("输入一个整数: "))
    result = 10 / num
except (ValueError, ZeroDivisionError) as e:
    print(f"捕获异常: {e}")

解释

  • 使用 (ValueError, ZeroDivisionError) 可以一次捕获多个异常类型。
  • 捕获的异常会存储在变量 e 中。

5. 自定义上下文管理器处理异常

通过实现 __enter____exit__ 方法,你可以创建自定义的上下文管理器,用于自动处理异常。

示例:自定义上下文管理器
class HelloWorldResource:
	# 和 with ... as x : 结合使用, 进入块时调用.
    def __enter__(self):
        print("你好世界: 资源已打开")
        return self
	# 和 with ... as x : 结合使用, 退出块时调用
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type: # 异常存在则可以在这里面处理
            print(f"捕获到世界BUG: {exc_value}")
        print("你好世界: 资源已关闭")
        return True  # 返回 True 表示异常已处理

# 使用自定义上下文管理器
with HelloWorldResource() as resource:
    raise ValueError("空间发生崩溃")  # 触发异常

解释

  • 如果 with 语句中的代码抛出异常,__exit__ 方法会捕获它。
  • 返回 True 表示异常已被处理,不会再向上传播。

执行结果

你好世界: 资源已打开
捕获到世界BUG: 空间发生崩溃
你好世界: 资源已关闭

6. 上下文变量传播异常

在某些复杂系统中,你可以将异常与上下文变量关联,以便在日志中提供更多调试信息。

示例:通过日志记录上下文信息
import logging
# 设定日志输出级别
logging.basicConfig(level=logging.INFO)

try:
    raise ValueError("这个值好像不对呀!!!")
except ValueError as e:
    logging.exception("我是日志, 我说这个值不太对劲!!!")

解释

  • 使用 logging.exception() 可以将堆栈信息记录在日志中,便于排查问题。

总结

学习小笔记

  • try-except:用于捕获代码中的异常,防止程序崩溃。
  • else:当 try 代码块没有异常时执行。
  • finally:无论是否出现异常都会执行,常用于资源清理。
  • 自定义异常:根据需要创建新的异常类型,用于抛出特定错误。

通过异常处理,我们可以让程序在面对错误时更加稳定可控,在代码中优雅处理异常问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值