第八章:Python3 之 异常与文件处理【从基础入门到底层原理+项目实战】

文章目录

  • [一、Python3 异常处理全解析](#一、Python3 异常处理全解析)
    • [1.1 异常基础概念](#1.1 异常基础概念)
      • [1.1.1 什么是异常](#1.1.1 什么是异常)
      • [1.1.2 常见内置异常类型](#1.1.2 常见内置异常类型)
      • [1.1.3 异常与语法错误的区别](#1.1.3 异常与语法错误的区别)
    • [1.2 基础异常处理:try-except](#1.2 基础异常处理:try-except)
      • [1.2.1 基础语法](#1.2.1 基础语法)
      • [1.2.2 基础实战代码](#1.2.2 基础实战代码)
    • [1.3 主动抛出异常:raise](#1.3 主动抛出异常:raise)
      • [1.3.1 语法格式](#1.3.1 语法格式)
      • [1.3.2 实战代码](#1.3.2 实战代码)
    • [1.4 捕获多个异常](#1.4 捕获多个异常)
    • [1.5 一个except块捕获多个异常](#1.5 一个except块捕获多个异常)
    • [1.6 捕获异常对象](#1.6 捕获异常对象)
    • [1.7 全捕捉异常(万能捕获)](#1.7 全捕捉异常(万能捕获))
    • [1.8 异常中的else子句](#1.8 异常中的else子句)
    • [1.9 自定义异常](#1.9 自定义异常)
    • [1.10 finally子句](#1.10 finally子句)
    • [1.11 异常与函数](#1.11 异常与函数)
  • [二、Python3 文件操作全解析](#二、Python3 文件操作全解析)
    • [2.1 文件操作基础:打开文件](#2.1 文件操作基础:打开文件)
    • [2.2 基本文件方法](#2.2 基本文件方法)
      • [2.2.1 读取文件方法](#2.2.1 读取文件方法)
      • [2.2.2 写入文件方法](#2.2.2 写入文件方法)
      • [2.2.3 其他常用文件方法](#2.2.3 其他常用文件方法)
    • [2.3 对文件内容进行迭代](#2.3 对文件内容进行迭代)
    • [2.4 StringIO 函数](#2.4 StringIO 函数)
    • [2.5 序列化与反序列化](#2.5 序列化与反序列化)
      • [2.5.1 json模块(通用推荐)](#2.5.1 json模块(通用推荐))
      • [2.5.2 pickle模块(Python专属)](#2.5.2 pickle模块(Python专属))
  • 三、Python异常底层原理深度解析
    • [3.1 异常的本质:异常对象与继承体系](#3.1 异常的本质:异常对象与继承体系)
    • [3.2 异常传播与栈展开机制](#3.2 异常传播与栈展开机制)
    • [3.3 CPython异常处理核心:异常表与零成本异常](#3.3 CPython异常处理核心:异常表与零成本异常)
    • [3.4 finally子句底层执行逻辑](#3.4 finally子句底层执行逻辑)
    • [3.5 异常对象核心属性](#3.5 异常对象核心属性)
  • 四、综合项目实战:日志文件处理器
    • [4.1 项目需求](#4.1 项目需求)
    • [4.2 项目完整代码(可直接运行)](#4.2 项目完整代码(可直接运行))
    • [4.3 项目说明](#4.3 项目说明)
  • 五、高频面试题汇总及答案
    • [5.1 基础异常面试题](#5.1 基础异常面试题)
      • [1. try-except-else-finally 执行顺序是什么?](#1. try-except-else-finally 执行顺序是什么?)
      • [2. 为什么不建议直接使用except捕获所有异常?](#2. 为什么不建议直接使用except捕获所有异常?)
      • [3. raise、assert 区别是什么?](#3. raise、assert 区别是什么?)
      • [4. 自定义异常为什么要继承Exception,而不是BaseException?](#4. 自定义异常为什么要继承Exception,而不是BaseException?)
    • [5.2 文件操作面试题](#5.2 文件操作面试题)
      • [1. with语句操作文件的优势是什么?](#1. with语句操作文件的优势是什么?)
      • [2. r、w、a 三种模式的区别?](#2. r、w、a 三种模式的区别?)
      • [3. json和pickle序列化的区别?](#3. json和pickle序列化的区别?)
      • [4. 如何高效读取大文件,避免内存溢出?](#4. 如何高效读取大文件,避免内存溢出?)
    • [5.3 综合进阶面试题](#5.3 综合进阶面试题)
      • [1. try块中有return语句,finally还会执行吗?执行顺序?](#1. try块中有return语句,finally还会执行吗?执行顺序?)
      • [2. 异常处理的性能开销如何?](#2. 异常处理的性能开销如何?)
      • [3. 文件操作中,编码格式为什么要指定utf-8?](#3. 文件操作中,编码格式为什么要指定utf-8?)
  • 六、总结

前言

在Python全栈开发、运维自动化、AI模型部署与数据处理等各类场景中,程序运行出错、文件读写失败都是无法避免的问题。无论是前端接口数据解析、后端业务逻辑执行、运维脚本批量处理文件,还是AI数据集读取与模型日志写入,一旦遇到未知错误、文件不存在、权限不足、数据格式异常等情况,程序就会直接崩溃终止,不仅影响业务流程,还会导致数据丢失、资源无法释放等严重问题。

异常处理与文件操作,是Python编程中最核心、最基础的必备技能,也是初中级开发者进阶、面试必考的核心知识点。文件操作负责完成数据的持久化存储、读取与交互,是程序与外部数据交互的桥梁;异常处理则负责兜底程序运行风险,让代码具备容错性、健壮性,保证程序在出错时依然能有序执行、释放资源、给出友好提示。

本文面向Python初级/中级开发者、后端工程师、前端开发者、运维人员、AI爱好者,从零开始系统性讲解Python3异常处理与文件操作的全套知识。从基础概念、语法用法,到底层实现原理、实战项目落地,再到高频面试题解析,全程搭配可直接运行、逐行注释的代码,兼顾通俗易懂与专业深度,帮助大家彻底掌握两大核心知识点,写出稳定、高效、容错性强的Python代码,轻松应对日常开发与面试考核。

全文知识点循序渐进,理论结合实操,无论是零基础入门,还是查漏补缺进阶,都能快速上手、学以致用。

一、Python3 异常处理全解析

1.1 异常基础概念

1.1.1 什么是异常

异常,就是程序运行过程中出现的非正常错误事件,这类错误会中断Python解释器的正常执行流程,导致程序直接崩溃终止。简单来说,异常就是程序"生病"了,如果不及时"医治"(处理),程序就会直接"倒下"。

Python是一门面向对象的编程语言,所有异常本质上都是类的实例对象,程序运行出错时,解释器会自动创建对应的异常对象并抛出,通知开发者程序出现错误。

1.1.2 常见内置异常类型

Python内置了丰富的异常类,所有异常都继承自顶层基类 BaseException ,日常开发中常用的异常均继承自 Exception 类,区分常见异常类型,能让我们更精准地捕获并处理错误。

异常类型 触发场景
SyntaxError 语法错误,代码不符合Python语法规范,解释器无法编译
NameError 访问未定义的变量、函数、类
TypeError 数据类型不匹配,比如对整数执行字符串方法、不同类型数据非法运算
ValueError 数据类型正确,但数值非法,比如将字符串转为整数时传入非数字字符
ZeroDivisionError 除数为0的数学运算
IndexError 访问列表、元组等序列超出索引范围
KeyError 访问字典中不存在的键
FileNotFoundError 打开不存在的文件
PermissionError 文件操作权限不足,比如只读文件执行写入操作
IOError 输入输出异常,Python3中已整合为OSError子类

1.1.3 异常与语法错误的区别

语法错误(SyntaxError):代码运行前,解释器检查语法不通过,直接报错,程序无法启动,属于编译期错误;

异常:代码语法完全正确,运行过程中因数据、外部环境、逻辑问题触发错误,属于运行期错误,也是本文重点讲解的内容。

示例:未处理异常的代码,触发后直接崩溃

python 复制代码
# 未做异常处理,除数为0触发ZeroDivisionError
def divide_num(a, b):
    # 直接执行除法,无容错
    return a / b

# 调用函数,传入除数0
if __name__ == '__main__':
    print(divide_num(10, 0))

运行结果:程序直接崩溃,抛出异常信息,后续代码无法执行。

1.2 基础异常处理:try-except

Python通过try-except 语句实现异常捕获与处理,核心逻辑:将可能出错的代码放入try代码块,一旦try块内触发异常,立即跳转到对应的except块执行容错逻辑,避免程序崩溃。

1.2.1 基础语法

python 复制代码
try:
    # 可能触发异常的代码块
    待检测的风险代码
except 异常类型:
    # 捕获到对应异常后,执行的容错处理代码
    异常处理逻辑

1.2.2 基础实战代码

python 复制代码
# 基础异常处理实战:处理除数为0异常
def divide_num(a, b):
    try:
        # 可能触发异常的代码
        result = a / b
        return result
    # 精准捕获ZeroDivisionError异常
    except ZeroDivisionError:
        print("错误:除数不能为0,请检查输入参数!")
        # 返回默认值,保证函数正常返回
        return None

# 测试调用
if __name__ == '__main__':
    # 测试正常情况
    print(divide_num(10, 2))
    # 测试异常情况
    print(divide_num(10, 0))
    # 程序不会崩溃,后续代码正常执行
    print("程序继续运行...")

运行结果:异常被捕获,程序正常执行,不会终止。

1.3 主动抛出异常:raise

除了解释器自动抛出异常,开发者还可以通过 raise 关键字主动抛出异常,常用于参数校验、业务逻辑判断,当数据不满足要求时,主动抛出异常提示调用方。

1.3.1 语法格式

python 复制代码
# 主动抛出指定异常,可附带异常描述信息
raise 异常类型("异常描述信息")

1.3.2 实战代码

python 复制代码
# 主动抛异常实战:用户年龄校验
def check_age(age):
    # 校验年龄是否为整数
    if not isinstance(age, int):
        # 主动抛出类型异常,附带提示信息
        raise TypeError("错误:年龄必须为整数类型!")
    # 校验年龄数值合法性
    if age < 0 or age > 150:
        # 主动抛出值异常
        raise ValueError("错误:年龄必须在0-150之间!")
    print("年龄校验通过!")

# 调用函数,捕获主动抛出的异常
if __name__ == '__main__':
    try:
        # 传入非法参数,触发主动抛异常
        check_age(200)
    except (TypeError, ValueError) as e:
        # 打印异常信息
        print(f"捕获异常:{e}")

1.4 捕获多个异常

当try代码块可能触发多种不同异常时,可通过多个except语句,分别捕获不同类型的异常,针对性做不同的容错处理。

语法格式

python 复制代码
try:
    风险代码
except 异常类型1:
    处理异常1
except 异常类型2:
    处理异常2
except 异常类型3:
    处理异常3

实战代码

python 复制代码
# 多异常捕获实战
def test_exception(num):
    try:
        # 可能触发TypeError、ZeroDivisionError
        result = 10 / num
        # 可能触发IndexError
        test_list = [1, 2, 3]
        print(test_list[result])
    except ZeroDivisionError:
        print("处理:除数不能为0")
    except TypeError:
        print("处理:数据类型不匹配,无法运算")
    except IndexError:
        print("处理:列表索引超出范围")

# 测试调用
if __name__ == '__main__':
    # 测试不同异常场景
    test_exception("a")   # 触发TypeError
    test_exception(0)     # 触发ZeroDivisionError
    test_exception(10)    # 触发IndexError

1.5 一个except块捕获多个异常

如果多种异常的处理逻辑完全一致,无需分开捕获,可将多个异常类型放入元组中,用一个except块统一捕获,简化代码。

实战代码

python 复制代码
# 单except块捕获多异常
def test_func(num):
    try:
        result = 10 / num
        test_list = [1, 2, 3]
        print(test_list[int(result)])
    # 同时捕获多种异常,处理逻辑一致
    except (ZeroDivisionError, TypeError, IndexError) as e:
        print(f"统一处理异常:{e}")

# 测试调用
if __name__ == '__main__':
    test_func(0)
    test_func("abc")

1.6 捕获异常对象

通过as 变量名 可以接收异常对象,获取异常的详细描述、类型等信息,便于日志记录、问题排查。

实战代码

python 复制代码
# 捕获异常对象,获取详细信息
def read_file(file_path):
    try:
        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()
            return content
    # 捕获异常并赋值给e,e就是异常对象
    except FileNotFoundError as e:
        # 打印异常类型、异常信息
        print(f"异常类型:{type(e).__name__}")
        print(f"异常详情:{e}")
    except PermissionError as e:
        print(f"权限异常:{e}")

# 测试调用
if __name__ == '__main__':
    read_file("test123.txt")

1.7 全捕捉异常(万能捕获)

不指定异常类型,直接使用 except: ,可以捕获所有类型的异常,属于兜底处理。注意:不建议滥用,会掩盖未知错误,不利于问题排查,仅用于兜底场景。

实战代码

python 复制代码
# 万能异常捕获
def test_all_exception():
    try:
        # 触发未知异常
        print(undefined_var)
    # 捕获所有异常,兜底处理
    except:
        print("程序出现未知异常,已兜底处理!")

if __name__ == '__main__':
    test_all_exception()

更规范的兜底写法:捕获Exception类,涵盖所有日常开发异常(BaseException包含系统退出类异常,不建议捕获)

python 复制代码
try:
    风险代码
except Exception as e:
    print(f"未知异常:{e}")

1.8 异常中的else子句

try-except搭配 else 子句,else块中的代码仅在try块无任何异常、正常执行完毕后才会运行,常用于处理无异常时的后续逻辑。

语法格式

python 复制代码
try:
    风险代码
except 异常类型:
    异常处理
else:
    # try块无异常,执行此处代码
    无异常后续逻辑

实战代码

python 复制代码
# 异常else子句实战
def calculate(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("除数不能为0")
    else:
        # 无异常时,打印计算结果
        print(f"计算结果:{result}")

if __name__ == '__main__':
    calculate(10, 2)  # 无异常,执行else
    calculate(10, 0)  # 有异常,不执行else

1.9 自定义异常

Python内置异常无法满足所有业务场景时,可通过继承Exception类自定义异常类,用于专属业务错误提示,让异常处理更贴合业务逻辑。

语法格式

python 复制代码
# 自定义异常类,继承Exception
class 自定义异常名(Exception):
    def __init__(self, message="异常描述"):
        # 调用父类构造方法
        super().__init__(message)
        self.message = message

实战代码

python 复制代码
# 自定义业务异常:余额不足异常
class BalanceInsufficientError(Exception):
    """自定义余额不足异常"""
    def __init__(self, message="账户余额不足,无法完成操作"):
        super().__init__(message)
        self.message = message

# 业务函数:账户取款
def withdraw_money(balance, money):
    if money > balance:
        # 抛出自定义异常
        raise BalanceInsufficientError(f"余额不足,当前余额:{balance},取款金额:{money}")
    balance -= money
    print(f"取款成功,剩余余额:{balance}")
    return balance

# 调用测试
if __name__ == '__main__':
    try:
        withdraw_money(100, 200)
    except BalanceInsufficientError as e:
        print(f"捕获自定义异常:{e}")

1.10 finally子句

finally 子句无论try块是否触发异常、是否捕获异常、是否return退出函数,一定会执行,常用于资源释放(文件关闭、数据库连接关闭、网络连接关闭),保证资源不泄露。

语法格式

python 复制代码
try:
    风险代码
except 异常类型:
    异常处理
finally:
    # 无论是否异常,必定执行
    资源释放、收尾代码

实战代码

python 复制代码
# finally子句实战:文件操作资源释放
def open_file_test(file_path):
    f = None
    try:
        f = open(file_path, "r", encoding="utf-8")
        content = f.read()
        print(content)
        return content
    except FileNotFoundError as e:
        print(f"文件不存在:{e}")
    finally:
        # 无论是否异常,必定关闭文件,释放资源
        if f:
            f.close()
            print("文件已关闭,资源释放成功")

if __name__ == '__main__':
    open_file_test("test.txt")

1.11 异常与函数

异常在函数调用栈中会向上传播:如果函数内部触发异常,且未在函数内捕获,异常会向上传递给函数调用方,直到被捕获;如果全程无捕获,程序最终崩溃。

实战代码:异常的函数间传播

python 复制代码
# 内层函数,未捕获异常
def func1():
    # 触发异常,未处理
    print(10 / 0)

# 中层函数,调用func1,未捕获异常
def func2():
    func1()

# 外层函数,调用func2,捕获异常
def func3():
    try:
        func2()
    except ZeroDivisionError as e:
        print(f"外层函数捕获到内层异常:{e}")

if __name__ == '__main__':
    func3()

核心结论:函数中出现异常,优先在当前函数内部处理;无法处理时,向上传递给调用方,实现分层异常处理。

二、Python3 文件操作全解析

2.1 文件操作基础:打开文件

Python通过内置函数 open() 打开文件,返回文件对象,后续所有文件读写操作都基于文件对象完成。

2.1.1 open()函数语法

python 复制代码
open(file, mode='r', encoding=None, buffering=-1, errors=None, newline=None, closefd=True, opener=None)

核心参数说明:

  • file:文件路径(相对路径/绝对路径)

  • mode:文件打开模式,决定读写权限

  • encoding:文件编码格式,推荐utf-8,读写中文必须指定

2.1.2 常用文件打开模式

模式 含义 注意事项
r 只读模式,默认模式 文件不存在,抛出FileNotFoundError
w 只写模式 文件不存在则创建,存在则清空原有内容
a 追加模式 文件不存在则创建,存在则在末尾追加内容
r+ 读写模式 文件不存在报错,可读写
w+ 读写模式 文件不存在创建,存在清空内容
a+ 追加读写模式 文件不存在创建,指针在末尾

2.1.3 基础打开文件方式

方式1:常规打开(需手动关闭文件)
python 复制代码
# 常规打开文件
f = open("test.txt", "r", encoding="utf-8")
# 文件读写操作
content = f.read()
print(content)
# 必须手动关闭,释放资源
f.close()
方式2:with语句(推荐,自动关闭文件)

with语句会自动调用close()方法,无需手动关闭,避免资源泄露,是Python文件操作的最佳实践。

python 复制代码
# with语句打开文件,自动关闭
with open("test.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print(content)
# 退出with代码块,文件自动关闭

2.2 基本文件方法

文件对象提供了丰富的读写方法,满足不同场景的文件操作需求。

2.2.1 读取文件方法

  1. read(size):读取指定字节数的内容,不指定size则读取全部内容

  2. readline():逐行读取,每次读取一行内容

  3. readlines():读取全部行,返回列表,每一行是列表的一个元素

python 复制代码
# 文件读取方法实战
with open("test.txt", "r", encoding="utf-8") as f:
    # 读取全部内容
    all_content = f.read()
    print("全部内容:", all_content)

    # 指针回到文件开头
    f.seek(0)

    # 逐行读取
    line1 = f.readline()
    line2 = f.readline()
    print("第一行:", line1)
    print("第二行:", line2)

    # 读取所有行,返回列表
    f.seek(0)
    lines = f.readlines()
    print("所有行列表:", lines)

2.2.2 写入文件方法

  1. write(str):写入字符串内容,返回写入字符数

  2. writelines(seq):写入字符串序列,无换行效果,需手动加换行符

python 复制代码
# 文件写入方法实战
# 写入单行内容
with open("write_test.txt", "w", encoding="utf-8") as f:
    f.write("Hello Python!n")
    f.write("文件写入测试n")

# 写入多行内容
with open("write_test.txt", "a", encoding="utf-8") as f:
    line_list = ["追加第一行n", "追加第二行n"]
    f.writelines(line_list)

2.2.3 其他常用文件方法

  • seek(offset):移动文件指针到指定位置

  • tell():返回当前文件指针位置

  • close():关闭文件,释放资源

2.3 对文件内容进行迭代

文件对象本身是可迭代对象,可直接通过for循环逐行迭代读取,内存占用低,适合读取大文件。

python 复制代码
# 文件内容迭代读取(大文件推荐)
with open("big_file.txt", "r", encoding="utf-8") as f:
    # 逐行迭代,无需一次性加载全部内容
    for line in f:
        # 去除每行末尾换行符
        line = line.strip()
        if line:
            print(line)

2.4 StringIO 函数

StringIO 是Python内置的内存文件操作工具,用于在内存中读写字符串,无需创建本地文件,适合临时字符串缓存、测试场景。

使用步骤

  1. 导入io模块中的StringIO

  2. 创建StringIO对象

  3. 调用读写方法操作内存数据

python 复制代码
# StringIO实战
from io import StringIO

# 创建内存文件对象
s_io = StringIO()
# 写入内容
s_io.write("内存字符串写入1n")
s_io.write("内存字符串写入2n")

# 获取内存中全部内容
# 指针移到开头
s_io.seek(0)
content = s_io.read()
print("StringIO内容:n", content)

# 关闭内存文件
s_io.close()

2.5 序列化与反序列化

文件只能直接读写字符串、字节流,无法直接存储列表、字典、对象等复杂数据,因此需要通过序列化 将复杂数据转为字节流/字符串,反序列化将字节流/字符串还原为复杂数据。

Python中常用 pickle 模块(二进制序列化,支持所有Python对象)、json 模块(文本序列化,通用跨语言)实现序列化与反序列化。

2.5.1 json模块(通用推荐)

  • json.dumps():将数据序列化为JSON字符串

  • json.loads():将JSON字符串反序列化为Python数据

  • json.dump():将数据序列化后直接写入文件

  • json.load():从文件读取JSON数据并反序列化

python 复制代码
# json序列化与反序列化实战
import json

# 待序列化的复杂数据
user_info = {
    "name": "张三",
    "age": 25,
    "hobby": ["读书", "编程", "运动"]
}

# 序列化:Python数据 -> JSON字符串,写入文件
with open("user.json", "w", encoding="utf-8") as f:
    # ensure_ascii=False 保证中文不乱码
    json.dump(user_info, f, ensure_ascii=False, indent=4)

# 反序列化:文件JSON数据 -> Python数据
with open("user.json", "r", encoding="utf-8") as f:
    load_data = json.load(f)
    print("反序列化后数据:", load_data)
    print("数据类型:", type(load_data))

2.5.2 pickle模块(Python专属)

  • pickle.dumps():序列化为字节流

  • pickle.loads():反序列化为Python对象

  • pickle.dump():序列化写入文件

  • pickle.load():从文件读取反序列化

python 复制代码
# pickle序列化实战
import pickle

# 测试数据
test_data = {"id": 1, "score": 95.5, "status": True}

# 序列化写入文件(二进制模式wb)
with open("data.pkl", "wb") as f:
    pickle.dump(test_data, f)

# 反序列化读取(二进制模式rb)
with open("data.pkl", "rb") as f:
    load_data = pickle.load(f)
    print("pickle反序列化数据:", load_data)

三、Python异常底层原理深度解析

3.1 异常的本质:异常对象与继承体系

Python中所有异常都是对象,完整的异常继承体系以 BaseException 为顶层基类,向下分为多个子类:

  • BaseException:所有异常的顶层基类,包含系统退出类异常(SystemExit、KeyboardInterrupt)

  • Exception:日常开发异常基类,所有业务异常、运行异常都继承此类,也是自定义异常的父类

当程序触发错误时,Python解释器会根据错误类型,自动实例化对应的异常类,创建异常对象,随后触发异常传播流程。

3.2 异常传播与栈展开机制

异常触发后,会沿着函数调用栈向上传播,也就是栈展开过程:

  1. 异常在当前函数触发,检查当前函数是否有try-except捕获

  2. 若无捕获,销毁当前函数栈帧,异常传递给上一层调用函数

  3. 重复上述流程,直到异常被捕获;若到达栈顶仍无捕获,程序终止,控制台打印异常堆栈

3.3 CPython异常处理核心:异常表与零成本异常

CPython解释器采用零成本异常处理机制,正常代码执行时,异常处理逻辑不占用任何性能开销,仅在异常触发时才启动异常处理流程。

编译器会将try-except代码块编译为异常表(Exception Table),异常表中记录try块代码偏移范围、对应异常处理块地址、栈深度等信息;异常触发时,解释器通过当前指令偏移量,在异常表中二分查找匹配的处理块,快速定位并执行异常处理逻辑。

3.4 finally子句底层执行逻辑

finally块的必定执行,底层依赖异常表的双条目设计:

  • 正常执行try块完毕,跳转执行finally块

  • try块触发异常,先执行异常处理,再跳转执行finally块

  • try块中有return语句,会先执行finally块,再执行return返回

3.5 异常对象核心属性

异常对象包含三个核心属性,用于问题排查:

  • args:异常参数,存储异常描述信息

  • traceback:异常回溯对象,记录异常触发的堆栈信息

  • context:隐式异常上下文,记录触发当前异常的原始异常

四、综合项目实战:日志文件处理器

4.1 项目需求

开发一个通用日志文件处理器,实现以下功能:

  1. 读取指定日志文件内容

  2. 筛选日志中的错误信息(ERROR级别)

  3. 将错误日志写入单独的错误日志文件

  4. 全程加入异常处理,保证程序健壮性

  5. 支持日志数据序列化存储

4.2 项目完整代码(可直接运行)

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
日志文件处理器项目实战
融合异常处理 + 文件操作 + 序列化全套知识
"""
import json
import os
from io import StringIO


class LogProcessError(Exception):
    """自定义日志处理异常"""
    def __init__(self, message="日志处理失败"):
        super().__init__(message)
        self.message = message


class LogProcessor:
    def __init__(self, log_path, error_log_path="error_log.json"):
        # 日志文件路径
        self.log_path = log_path
        # 错误日志存储路径
        self.error_log_path = error_log_path
        # 存储错误日志列表
        self.error_log_list = []

    def read_log_file(self):
        """
        读取日志文件,逐行处理
        :return: 日志内容列表
        """
        try:
            # 判断文件是否存在
            if not os.path.exists(self.log_path):
                raise LogProcessError(f"日志文件不存在:{self.log_path}")

            # 迭代读取大文件,避免内存溢出
            with open(self.log_path, "r", encoding="utf-8") as f:
                log_content = [line.strip() for line in f if line.strip()]
            print(f"日志文件读取成功,共{len(log_content)}行")
            return log_content

        except PermissionError as e:
            raise LogProcessError(f"日志文件权限不足:{e}")
        except Exception as e:
            raise LogProcessError(f"读取日志文件失败:{e}")

    def filter_error_log(self, log_content):
        """
        筛选ERROR级别日志
        :param log_content: 日志内容列表
        :return: 错误日志列表
        """
        if not log_content:
            raise LogProcessError("日志内容为空,无法筛选")

        for index, line in enumerate(log_content):
            if "ERROR" in line:
                error_info = {
                    "line_num": index + 1,
                    "content": line,
                    "type": "ERROR"
                }
                self.error_log_list.append(error_info)

        print(f"筛选完成,共找到{len(self.error_log_list)}条错误日志")
        return self.error_log_list

    def save_error_log(self):
        """
        序列化保存错误日志
        """
        try:
            with open(self.error_log_path, "w", encoding="utf-8") as f:
                json.dump(self.error_log_list, f, ensure_ascii=False, indent=4)
            print(f"错误日志已保存至:{self.error_log_path}")
        except Exception as e:
            raise LogProcessError(f"保存错误日志失败:{e}")

    def run(self):
        """
        启动日志处理流程,统一异常捕获
        """
        try:
            # 1. 读取日志
            content = self.read_log_file()
            # 2. 筛选错误日志
            self.filter_error_log(content)
            # 3. 保存错误日志
            self.save_error_log()
            print("日志处理任务全部完成!")
        except LogProcessError as e:
            print(f"日志处理业务异常:{e}")
        except Exception as e:
            print(f"系统未知异常:{e}")


# 测试运行
if __name__ == '__main__':
    # 1. 先创建测试日志文件
    test_log = """2025-03-28 10:00:00 INFO 系统启动成功
2025-03-28 10:05:00 ERROR 数据库连接失败
2025-03-28 10:10:00 INFO 用户登录成功
2025-03-28 10:15:00 ERROR 文件读取超时
"""
    # 写入测试日志
    with open("system.log", "w", encoding="utf-8") as f:
        f.write(test_log)

    # 2. 启动日志处理器
    processor = LogProcessor("system.log")
    processor.run()

4.3 项目说明

  1. 项目融合自定义异常、多异常捕获、finally底层逻辑、文件读写、JSON序列化全套知识点

  2. 采用面向对象封装,代码可复用、易扩展

  3. 全程异常兜底,避免文件操作、数据处理触发崩溃

  4. 支持大文件迭代读取,内存占用低,适配运维、后端日志处理场景

五、高频面试题汇总及答案

5.1 基础异常面试题

1. try-except-else-finally 执行顺序是什么?

答案:先执行try块;如果触发异常,执行except块;如果无异常,执行else块;无论是否异常、是否执行except/else,最后必定执行finally块。

2. 为什么不建议直接使用except捕获所有异常?

答案:会捕获所有未知异常、系统异常,掩盖代码隐藏问题,不利于问题排查;建议精准捕获具体异常,最后用Exception做兜底。

3. raise、assert 区别是什么?

答案:raise用于主动抛出任意异常,可自定义异常信息;assert是断言,条件不成立时抛出AssertionError,多用于开发调试、代码校验,生产环境不建议滥用。

4. 自定义异常为什么要继承Exception,而不是BaseException?

答案:BaseException包含SystemExit、KeyboardInterrupt等系统退出异常,捕获后会导致程序无法正常退出;日常业务异常继承Exception即可,不影响系统异常处理。

5.2 文件操作面试题

1. with语句操作文件的优势是什么?

答案:with语句基于上下文管理器,自动调用close()方法关闭文件、释放资源,无需手动处理,避免资源泄露;代码更简洁,容错性更强。

2. r、w、a 三种模式的区别?

答案:r是只读模式,文件不存在报错;w是只写模式,文件不存在创建,存在清空内容;a是追加模式,文件不存在创建,存在在末尾追加内容。

3. json和pickle序列化的区别?

答案:json是文本序列化,跨语言通用,仅支持基础数据类型;pickle是二进制序列化,Python专属,支持所有Python对象,但无法跨语言。

4. 如何高效读取大文件,避免内存溢出?

答案:不要用read()一次性读取全部内容,通过for循环直接迭代文件对象,逐行读取;或用read(size)分块读取,减少内存占用。

5.3 综合进阶面试题

1. try块中有return语句,finally还会执行吗?执行顺序?

答案:finally一定会执行。执行顺序:先执行try块代码,遇到return前,先执行finally块,再执行return返回结果。

2. 异常处理的性能开销如何?

答案:CPython采用零成本异常机制,正常执行时无任何性能开销;仅异常触发时,会有栈展开、异常查找的开销,日常开发中完全可以忽略,不要用异常代替正常业务判断。

3. 文件操作中,编码格式为什么要指定utf-8?

答案:不指定编码时,Python会使用系统默认编码,Windows默认GBK,Linux默认utf-8,跨平台会出现中文乱码;指定utf-8可保证多平台编码一致。

六、总结

本文系统性讲解了Python3异常处理与文件操作两大核心知识点,从基础概念、语法用法,到底层原理、项目实战、面试解析,全方位覆盖初中级开发者必备技能。

异常处理方面,我们掌握了try-except-else-finally全套语法、主动抛异常、自定义异常、异常传播机制,以及CPython底层异常处理原理,学会写出容错性强、健壮稳定的代码;文件操作方面,我们掌握了文件读写、迭代处理、内存文件StringIO、数据序列化与反序列化,精通大文件处理、资源释放最佳实践。

在实际开发中,无论是后端接口开发、运维脚本编写、AI数据处理,还是前端数据持久化,都要养成异常处理的编程习惯,优先使用with语句操作文件,精准捕获异常,避免程序崩溃、资源泄露。

异常处理与文件操作是Python编程的基石,熟练掌握本文知识点,不仅能提升日常开发效率、减少代码BUG,还能轻松应对各类Python面试,为后续进阶高阶编程、框架学习打下坚实基础。后续可进一步结合上下文管理器、多线程文件操作、日志模块等知识,深化学习,打造更高效的Python程序。


🙌 感谢您读到这里!

🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近您与目标的距离。

💡 如果本文对你有帮助,不妨 👍点赞、📌 收藏、📤 分享 给更多需要的朋友!

💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 !

🔔 关注我,不错过下一篇干货!我们下期再见!

相关推荐
2301_816651222 小时前
用户认证与授权:使用JWT保护你的API
jvm·数据库·python
Sunshine for you2 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
wefly20172 小时前
m3u8live.cn:免安装 HLS 在线播放器,流媒体调试效率神器
开发语言·javascript·python·django·ecmascript·hls.js 原理·m3u8 解析
大叔_爱编程2 小时前
基于用户评论的热点问题挖掘与反馈分析系统-django+spider+uniapp
python·django·uni-app·毕业设计·源码·课程设计·spider
第一程序员2 小时前
Python与AR/VR:非科班转码者的指南
python·github
工业互联网专业2 小时前
基于Python的广东旅游数据分析_flask+spider
python·数据分析·flask·毕业设计·源码·课程设计·spider
05大叔2 小时前
Pyhton自带库和三方库
开发语言·python
wefly20173 小时前
jsontop.cn:一站式 JSON 全能工具集,开发全流程效率神器
前端·javascript·python·django·json·json在线转换
li99yo6 小时前
3DGS的复现
图像处理·pytorch·经验分享·python·3d·conda·pip