异常处理#

异常处理是 Python 中处理错误和异常情况的机制。良好的异常处理可以使程序更加健壮。

什么是异常?#

异常是程序运行时发生的错误。当 Python 遇到错误时,会抛出(raise)一个异常。

# 常见的异常
1 / 0              # ZeroDivisionError: division by zero
int("abc")         # ValueError: invalid literal for int()
x[10]              # IndexError: list index out of range
d["key"]           # KeyError: 'key'(如果key不存在)

try-except 语句#

基本语法#

try:
    # 可能出错的代码
    result = 10 / 0
except ZeroDivisionError:
    # 处理异常
    print("除零错误!")

# 程序继续执行
print("程序继续运行")

捕获多个异常#

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

# 捕获多个异常类型
try:
    # 代码
    pass
except (ValueError, TypeError):
    print("值错误或类型错误")

# 捕获所有异常(不推荐,除非必要)
try:
    # 代码
    pass
except Exception as e:
    print(f"发生错误: {e}")

获取异常信息#

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"错误类型: {type(e).__name__}")
    print(f"错误信息: {e}")
    print(f"错误详情: {e.args}")

try-except-else 语句#

else 子句在没有异常时执行。

try:
    x = int(input("输入一个数字: "))
    result = 10 / x
except ValueError:
    print("输入的不是数字!")
except ZeroDivisionError:
    print("不能除以零!")
else:
    print(f"结果是: {result}")  # 只有在没有异常时执行

try-except-finally 语句#

finally 子句无论是否发生异常都会执行。

try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("文件不存在!")
finally:
    file.close()  # 无论是否发生异常都会执行
    print("清理工作完成")

使用 with 语句(推荐)#

# 使用with语句自动处理资源清理
try:
    with open("data.txt", "r") as file:
        content = file.read()
except FileNotFoundError:
    print("文件不存在!")
# 文件会自动关闭,不需要finally

常见异常类型#

# ValueError - 值错误
int("abc")  # ValueError: invalid literal for int()

# TypeError - 类型错误
"hello" + 5  # TypeError: can only concatenate str (not "int") to str

# IndexError - 索引错误
lst = [1, 2, 3]
lst[10]  # IndexError: list index out of range

# KeyError - 键错误
d = {"a": 1}
d["b"]  # KeyError: 'b'

# AttributeError - 属性错误
x = 10
x.append(5)  # AttributeError: 'int' object has no attribute 'append'

# NameError - 名称错误
print(undefined_var)  # NameError: name 'undefined_var' is not defined

# ZeroDivisionError - 除零错误
10 / 0  # ZeroDivisionError: division by zero

# FileNotFoundError - 文件未找到
open("nonexistent.txt")  # FileNotFoundError: [Errno 2] No such file or directory

raise 语句#

使用 raise 可以主动抛出异常。

# 抛出异常
x = -1
if x < 0:
    raise ValueError("x不能为负数")

# 重新抛出异常
try:
    x = 10 / 0
except ZeroDivisionError:
    print("捕获到异常")
    raise  # 重新抛出异常

# 抛出不同类型的异常
try:
    x = 10 / 0
except ZeroDivisionError:
    raise ValueError("转换错误")  # 抛出新的异常类型

自定义异常#

Python 中的异常都是类,抛出异常就是创建该异常类的实例

自定义异常类可以更好地表达特定的错误情况,提高代码可读性和错误处理的精确性。

基本定义#

# 简单自定义异常
class MyCustomError(Exception):
    """自定义异常"""
    pass

# 带自定义消息的异常
class ValidationError(Exception):
    """验证错误"""
    def __init__(self, message, field):
        self.message = message
        self.field = field
        super().__init__(self.message)

# 使用自定义异常
def validate_age(age):
    if age < 0:
        raise ValidationError("年龄不能为负数", "age")
    if age > 150:
        raise ValidationError("年龄不合理", "age")

try:
    validate_age(-5)
except ValidationError as e:
    print(f"验证失败: {e.message}, 字段: {e.field}")

异常继承层次#

# 定义异常基类
class APIError(Exception):
    """API错误基类"""
    def __init__(self, message, status_code=500):
        self.message = message
        self.status_code = status_code
        super().__init__(self.message)

# 定义具体异常
class NotFoundError(APIError):
    """资源未找到"""
    def __init__(self, message="资源未找到"):
        super().__init__(message, status_code=404)

class AuthenticationError(APIError):
    """认证错误"""
    def __init__(self, message="认证失败"):
        super().__init__(message, status_code=401)

# 使用
try:
    raise NotFoundError("用户不存在")
except APIError as e:
    print(f"API错误: {e.message}, 状态码: {e.status_code}")

带额外属性的异常#

class FileProcessingError(Exception):
    """文件处理错误"""
    def __init__(self, message, filename, line_number=None):
        self.message = message
        self.filename = filename
        self.line_number = line_number
        super().__init__(self.message)
    
    def __str__(self):
        """自定义异常显示"""
        base_msg = f"{self.message} (文件: {self.filename}"
        if self.line_number:
            base_msg += f", 行号: {self.line_number}"
        return base_msg + ")"

# 使用
try:
    raise FileProcessingError("格式错误", "data.txt", line_number=42)
except FileProcessingError as e:
    print(e)  # 格式错误 (文件: data.txt, 行号: 42)

使用规则#

  1. 继承 Exception:自定义异常必须继承 Exception 或其子类
  2. 命名规范:异常类名以 ErrorException 结尾
  3. 文档字符串:使用文档字符串说明异常用途
  4. 初始化方法:重写 __init__ 添加自定义属性,调用 super().__init__()
  5. 异常消息:通过 super().__init__(message) 设置异常消息
  6. 继承层次:可以创建异常基类和具体异常类,形成异常层次结构
  7. 自定义显示:重写 __str__ 方法自定义异常显示格式

异常链#

Python 3 支持异常链,可以保留原始异常信息。

try:
    x = 10 / 0
except ZeroDivisionError as e:
    raise ValueError("处理错误") from e
    # 输出会显示两个异常:ValueError 和 ZeroDivisionError

# 使用 from None 隐藏原始异常
try:
    x = 10 / 0
except ZeroDivisionError:
    raise ValueError("处理错误") from None
    # 只显示 ValueError

断言(assert)#

assert 语句用于调试,如果条件为 False,会抛出 AssertionError

# 基本语法:assert 条件, "错误信息"
x = 5
assert x > 0, "x必须大于0"

x = -1
assert x > 0, "x必须大于0"  # AssertionError: x必须大于0

# 注意:在生产环境中,assert可能被禁用(使用 -O 选项)
# 不要用assert来处理应该用异常处理的错误

异常处理最佳实践#

1. 具体化异常类型#

# 不好的做法
try:
    # 代码
    pass
except:
    pass  # 捕获所有异常,隐藏了问题

# 好的做法
try:
    # 代码
    pass
except SpecificError as e:
    # 处理特定异常
    pass

2. 不要过度使用异常#

# 不好的做法:用异常处理正常流程
try:
    value = my_dict["key"]
except KeyError:
    value = None

# 好的做法:先检查
value = my_dict.get("key", None)

3. 清理资源#

# 使用with语句或finally确保资源被清理
try:
    file = open("data.txt")
    # 处理文件
finally:
    file.close()

# 或使用with语句(推荐)
with open("data.txt") as file:
    # 处理文件
    pass

4. 提供有意义的错误信息#

# 不好的做法
raise ValueError("错误")

# 好的做法
raise ValueError(f"年龄 {age} 不在有效范围内(0-150)")

5. 记录异常#

import logging

try:
    # 代码
    pass
except Exception as e:
    logging.error(f"发生错误: {e}", exc_info=True)
    raise  # 重新抛出异常

上下文管理器#

上下文管理器用于确保资源被正确获取和释放。

# 自定义上下文管理器
class MyContextManager:
    def __enter__(self):
        print("进入上下文")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("退出上下文")
        return False  # 返回False表示不抑制异常

# 使用
with MyContextManager() as cm:
    print("在上下文中")
# 输出:
# 进入上下文
# 在上下文中
# 退出上下文

实际应用示例#

def safe_divide(a, b):
    """安全除法"""
    try:
        result = a / b
    except ZeroDivisionError:
        print("错误:除数不能为零")
        return None
    except TypeError:
        print("错误:参数类型不正确")
        return None
    else:
        return result

# 文件处理
def read_file(filename):
    """读取文件"""
    try:
        with open(filename, "r", encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        print(f"错误:文件 {filename} 不存在")
        return None
    except PermissionError:
        print(f"错误:没有权限读取文件 {filename}")
        return None
    except Exception as e:
        print(f"未知错误: {e}")
        return None

下一步#

掌握了异常处理后,可以学习:

  1. 模块和包 - 学习如何组织代码文件
  2. 文件操作 - 学习文件读写操作