百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

Python教程(二十):异常处理 try、except、finally

ztj100 2025-08-01 22:14 4 浏览 0 评论

昨天,我们学习了文件操作,掌握了读写文件的基础技能。今天,我们将学习异常处理 — Python中处理错误和异常情况的重要机制。

异常处理让您的程序更加健壮,能够优雅地处理各种错误情况,而不是让程序崩溃。


今天您将学习什么

  • 什么是异常以及异常的类型
  • try-except语句的基本用法
  • 多种异常处理方式
  • finally和else子句
  • 自定义异常类
  • 真实世界示例:错误处理、资源管理、数据验证

什么是异常?

异常是程序执行过程中发生的错误或异常情况。当Python遇到无法处理的错误时,会抛出一个异常对象。

常见的异常类型:

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

# TypeError - 类型错误
len(123)    # TypeError: object of type 'int' has no len()

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

# KeyError - 键错误
data = {"name": "Alice"}
data["age"]  # KeyError: 'age'

# FileNotFoundError - 文件未找到
open("nonexistent.txt")  # FileNotFoundError

1. 基本的try-except语句

捕获单个异常

try:
    number = int(input("请输入一个数字:"))
    result = 10 / number
    print(f"结果是:{result}")
except ValueError:
    print("输入无效,请输入一个有效的数字")
except ZeroDivisionError:
    print("不能除以零")

捕获多个异常

try:
    number = int(input("请输入一个数字:"))
    result = 10 / number
    print(f"结果是:{result}")
except (ValueError, ZeroDivisionError) as e:
    print(f"发生错误:{e}")

捕获所有异常(不推荐)

try:
    number = int(input("请输入一个数字:"))
    result = 10 / number
    print(f"结果是:{result}")
except Exception as e:
    print(f"发生未知错误:{e}")

2. try-except-else-finally

完整的异常处理结构

try:
    # 可能出错的代码
    number = int(input("请输入一个数字:"))
    result = 10 / number
except ValueError:
    # 处理ValueError异常
    print("输入无效,请输入一个有效的数字")
except ZeroDivisionError:
    # 处理ZeroDivisionError异常
    print("不能除以零")
else:
    # 没有异常时执行
    print(f"计算成功,结果是:{result}")
finally:
    # 无论是否有异常都会执行
    print("程序执行完毕")

文件操作的异常处理

try:
    with open('data.txt', 'r', encoding='utf-8') as file:
        content = file.read()
        print("文件读取成功")
except FileNotFoundError:
    print("文件不存在")
except PermissionError:
    print("没有权限读取文件")
except UnicodeDecodeError:
    print("文件编码错误")
else:
    print(f"文件内容:{content}")
finally:
    print("文件操作完成")

3. 异常处理的最佳实践

具体的异常处理

# 好的做法:捕获具体异常
try:
    data = {"name": "Alice"}
    age = data["age"]
except KeyError:
    print("键不存在")

# 不好的做法:捕获所有异常
try:
    data = {"name": "Alice"}
    age = data["age"]
except Exception:  # 太宽泛
    print("发生错误")

异常信息的处理

try:
    number = int("abc")
except ValueError as e:
    print(f"值错误:{e}")
    print(f"错误类型:{type(e).__name__}")
    # 可以记录日志或发送错误报告

真实世界示例1:数据验证系统

class DataValidator:
    def __init__(self):
        self.errors = []
    
    def validate_age(self, age_str):
        """验证年龄"""
        try:
            age = int(age_str)
            if age < 0 or age > 150:
                raise ValueError("年龄必须在0-150之间")
            return age
        except ValueError as e:
            self.errors.append(f"年龄验证失败:{e}")
            return None
    
    def validate_email(self, email):
        """验证邮箱格式"""
        try:
            if '@' not in email or '.' not in email:
                raise ValueError("邮箱格式不正确")
            if len(email) < 5:
                raise ValueError("邮箱长度太短")
            return email
        except ValueError as e:
            self.errors.append(f"邮箱验证失败:{e}")
            return None
    
    def validate_user_data(self, user_data):
        """验证用户数据"""
        validated_data = {}
        
        # 验证姓名
        try:
            name = user_data.get('name', '').strip()
            if not name:
                raise ValueError("姓名不能为空")
            if len(name) < 2:
                raise ValueError("姓名长度太短")
            validated_data['name'] = name
        except ValueError as e:
            self.errors.append(f"姓名验证失败:{e}")
        
        # 验证年龄
        age = self.validate_age(user_data.get('age', ''))
        if age is not None:
            validated_data['age'] = age
        
        # 验证邮箱
        email = self.validate_email(user_data.get('email', ''))
        if email is not None:
            validated_data['email'] = email
        
        return validated_data
    
    def get_errors(self):
        """获取所有错误信息"""
        return self.errors.copy()
    
    def has_errors(self):
        """检查是否有错误"""
        return len(self.errors) > 0

# 使用示例
validator = DataValidator()

# 测试数据
test_data = {
    'name': 'A',  # 姓名太短
    'age': 'abc',  # 年龄格式错误
    'email': 'invalid-email'  # 邮箱格式错误
}

validated_data = validator.validate_user_data(test_data)

if validator.has_errors():
    print("验证失败,错误信息:")
    for error in validator.get_errors():
        print(f"  - {error}")
else:
    print("验证成功!")
    print(f"有效数据:{validated_data}")

真实世界示例2:安全的文件操作

import os
from pathlib import Path

class SafeFileManager:
    def __init__(self, base_dir="data"):
        self.base_dir = Path(base_dir)
        self.base_dir.mkdir(exist_ok=True)
    
    def safe_read_file(self, filename):
        """安全读取文件"""
        file_path = self.base_dir / filename
        
        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                content = file.read()
                return {"success": True, "data": content}
        except FileNotFoundError:
            return {"success": False, "error": "文件不存在"}
        except PermissionError:
            return {"success": False, "error": "没有读取权限"}
        except UnicodeDecodeError:
            return {"success": False, "error": "文件编码错误"}
        except Exception as e:
            return {"success": False, "error": f"未知错误:{e}"}
    
    def safe_write_file(self, filename, content):
        """安全写入文件"""
        file_path = self.base_dir / filename
        
        try:
            # 检查目录是否存在
            file_path.parent.mkdir(parents=True, exist_ok=True)
            
            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(content)
                return {"success": True, "message": "文件写入成功"}
        except PermissionError:
            return {"success": False, "error": "没有写入权限"}
        except OSError as e:
            return {"success": False, "error": f"系统错误:{e}"}
        except Exception as e:
            return {"success": False, "error": f"未知错误:{e}"}
    
    def safe_delete_file(self, filename):
        """安全删除文件"""
        file_path = self.base_dir / filename
        
        try:
            if file_path.exists():
                file_path.unlink()
                return {"success": True, "message": "文件删除成功"}
            else:
                return {"success": False, "error": "文件不存在"}
        except PermissionError:
            return {"success": False, "error": "没有删除权限"}
        except Exception as e:
            return {"success": False, "error": f"删除失败:{e}"}

# 使用示例
file_manager = SafeFileManager("test_data")

# 写入文件
result = file_manager.safe_write_file("test.txt", "Hello, World!")
print(result)

# 读取文件
result = file_manager.safe_read_file("test.txt")
if result["success"]:
    print(f"文件内容:{result['data']}")
else:
    print(f"读取失败:{result['error']}")

# 删除文件
result = file_manager.safe_delete_file("test.txt")
print(result)

真实世界示例3:计算器应用

class Calculator:
    def __init__(self):
        self.history = []
    
    def add(self, a, b):
        """加法运算"""
        try:
            result = float(a) + float(b)
            self.history.append(f"{a} + {b} = {result}")
            return result
        except (ValueError, TypeError) as e:
            raise ValueError(f"无效的操作数:{e}")
    
    def subtract(self, a, b):
        """减法运算"""
        try:
            result = float(a) - float(b)
            self.history.append(f"{a} - {b} = {result}")
            return result
        except (ValueError, TypeError) as e:
            raise ValueError(f"无效的操作数:{e}")
    
    def multiply(self, a, b):
        """乘法运算"""
        try:
            result = float(a) * float(b)
            self.history.append(f"{a} * {b} = {result}")
            return result
        except (ValueError, TypeError) as e:
            raise ValueError(f"无效的操作数:{e}")
    
    def divide(self, a, b):
        """除法运算"""
        try:
            if float(b) == 0:
                raise ValueError("除数不能为零")
            result = float(a) / float(b)
            self.history.append(f"{a} / {b} = {result}")
            return result
        except (ValueError, TypeError) as e:
            raise ValueError(f"无效的操作数:{e}")
    
    def calculate(self, expression):
        """计算表达式"""
        try:
            # 简单的表达式解析(仅支持基本运算)
            parts = expression.split()
            if len(parts) != 3:
                raise ValueError("表达式格式错误,应为:数字 运算符 数字")
            
            a, operator, b = parts
            
            if operator == '+':
                return self.add(a, b)
            elif operator == '-':
                return self.subtract(a, b)
            elif operator == '*':
                return self.multiply(a, b)
            elif operator == '/':
                return self.divide(a, b)
            else:
                raise ValueError(f"不支持的运算符:{operator}")
        
        except ValueError as e:
            print(f"计算错误:{e}")
            return None
    
    def get_history(self):
        """获取计算历史"""
        return self.history.copy()

# 使用示例
calc = Calculator()

# 测试各种计算
test_expressions = [
    "10 + 5",
    "10 - 3",
    "4 * 6",
    "15 / 3",
    "10 / 0",  # 会出错
    "abc + 5",  # 会出错
    "10 + 5 + 3"  # 格式错误
]

for expr in test_expressions:
    print(f"计算:{expr}")
    result = calc.calculate(expr)
    if result is not None:
        print(f"结果:{result}")
    print()

# 显示计算历史
print("计算历史:")
for entry in calc.get_history():
    print(f"  {entry}")

4. 自定义异常类

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

class DatabaseError(Exception):
    """自定义数据库错误异常"""
    def __init__(self, message, error_code=None):
        self.message = message
        self.error_code = error_code
        super().__init__(self.message)

# 使用自定义异常
def validate_user_age(age):
    try:
        age_int = int(age)
        if age_int < 0:
            raise ValidationError("年龄不能为负数", "age")
        if age_int > 150:
            raise ValidationError("年龄不能超过150", "age")
        return age_int
    except ValueError:
        raise ValidationError("年龄必须是数字", "age")

# 测试自定义异常
try:
    age = validate_user_age("200")
except ValidationError as e:
    print(f"验证错误:{e.message}")
    if e.field:
        print(f"错误字段:{e.field}")

异常处理的最佳实践

推荐做法:

  • 捕获具体的异常类型
  • 提供有意义的错误信息
  • 使用finally确保资源清理
  • 适当记录错误日志

避免的做法:

  • 捕获所有异常而不处理
  • 忽略异常
  • 在except块中使用pass
  • 过度使用异常处理

异常处理的高级技巧

上下文管理器

class DatabaseConnection:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.connection = None
    
    def __enter__(self):
        try:
            # 模拟数据库连接
            print(f"连接到数据库 {self.host}:{self.port}")
            self.connection = "connected"
            return self
        except Exception as e:
            print(f"连接失败:{e}")
            raise
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.connection:
            print("关闭数据库连接")
            self.connection = None

# 使用上下文管理器
try:
    with DatabaseConnection("localhost", 5432) as db:
        print("执行数据库操作")
        # 模拟操作失败
        raise Exception("操作失败")
except Exception as e:
    print(f"操作失败:{e}")

回顾

今天您学习了:

  • 异常的基本概念和类型
  • try-except语句的使用
  • 完整的异常处理结构
  • 自定义异常类
  • 真实世界应用:数据验证、文件操作、计算器

异常处理是编写健壮程序的重要技能,掌握这些知识将让您的程序更加可靠!

相关推荐

Java的SPI机制详解

作者:京东物流杨苇苇1.SPI简介SPI(ServiceProvicerInterface)是Java语言提供的一种接口发现机制,用来实现接口和接口实现的解耦。简单来说,就是系统只需要定义接口规...

90%的Java程序员都忽视的内部类使用不当导致内存泄露!

...

一文读懂 Spring Boot 启动原理,开发效率飙升!

在当今的Java开发领域,SpringBoot无疑是最热门的框架之一。它以其“约定大于配置”的理念,让开发者能够快速搭建和启动应用,极大地提高了开发效率。但是,你是否真正了解Spring...

ServiceLoader

ServiceLoader是Java提供的一种服务发现机制(ServiceProviderInterface,SPI)...

深入探索 Spring Boot3 中的自定义扩展操作

在当今互联网软件开发领域,SpringBoot无疑是最受欢迎的框架之一。随着其版本迭代至SpringBoot3,它为开发者们带来了更多强大的功能和特性,其中自定义扩展操作更是为我们在项目开发中...

Spring Boot启动过程全面解析:从入门到精通

一、SpringBoot概述SpringBoot是一个基于Spring框架的快速开发脚手架,它通过"约定优于配置"的原则简化了Spring应用的初始搭建和开发过程。...

Spring Boot 3.x 自定义 Starter 详解

今天星期六,继续卷springboot3.x。在SpringBoot3.x中,自定义Starter是封装和共享通用功能、实现“约定优于配置”理念的强大机制。通过创建自己的Starte...

Spring Boot 的 3 种动态 Bean 注入技巧

在SpringBoot开发中,动态注入Bean是一种强大的技术,它允许我们根据特定条件或运行时环境灵活地创建和管理Bean。相比于传统的静态Bean定义,动态注入提供了更高的灵活性和可...

大佬用4000字带你彻底理解SpringBoot的运行原理!

SpringBoot的运行原理从前面创建的SpringBoot应用示例中可以看到,启动一个SpringBoot工程都是从SpringApplication.run()方法开始的。这个方法具体完成...

Springboot是如何实现自动配置的

SpringBoot的自动配置功能极大地简化了基于Spring的应用程序的配置过程。它能够根据类路径中的依赖和配置文件中的属性,自动配置应用程序。下面是SpringBoot实现自动配置的...

Spring Boot3.x 应用的生命周期深度解析

SpringBoot应用的生命周期可以清晰地划分为三个主要阶段:启动阶段(Startup)...

Springboot 启动流程及各类事件生命周期那点事

前言本文通过Springboot启动方法分析SpringApplication逻辑。从静态run方法执行到各个阶段发布不同事件完成整个应用启动。...

Spring框架基础知识-常用的接口1

BeanDefinition基本概念BeanDefinition是Spring框架中描述bean配置信息的核心接口,它包含了创建bean实例所需的所有元数据。...

一家拥有 158 年历史的公司遭遇索赔,被迫关闭!

...

Java 技术岗面试全景备战!从基础到架构的系统性通关攻略分享

Java技术岗的面试往往是一项多维度的能力检验。本文将会从核心知识点、项目经验到面试策略,为你梳理一份系统性的备战攻略!...

取消回复欢迎 发表评论: