当前位置:首页>python>Python(三十七) 上下文管理器完整教程

Python(三十七) 上下文管理器完整教程

  • 2026-08-18 23:11:18
Python(三十七) 上下文管理器完整教程
Python(三十七) 上下文管理器完整教程

目录
1.
开篇:从"借书"理解上下文管理器
2.
with 语句的核心原理
3.
基于类的上下文管理器:enter 与 exit
4.
exit 的三个异常参数详解
5.
contextlib 标准库工具
6.
综合实战案例
7.
常见误区与面试题

一、开篇:从"借书"理解上下文管理器
1.1 生活化类比

你去图书馆借书看,完整的流程是:

无论书好不好看、无论你中途接不接电话,第三步一定要做——书必须还回去,否则其他人借不到。

编程中也有类似的"借还"场景:

场景
获取资源
释放资源
操作文件
open()close()
操作数据库
connect()connection.close()
获取锁
lock.acquire()lock.release()
网络连接
socket.connect()socket.close()

**上下文管理器(Context Manager)**就是帮你自动"归还"资源的机制。with 语句就是使用这个机制的语法。

1.2 没有 with 的世界(反面教材)
# 场景:读取文件内容
# === 错误写法1:忘了关闭文件 ===
f = open("data.txt""r", encoding="utf-8")
content = f.read()
# 忘了 f.close()!文件句柄泄漏,可能导致其他程序无法访问该文件
# === 错误写法2:中途出错,close 不会执行 ===
f = open("data.txt""r", encoding="utf-8")
content = f.read()
# 假设这里执行了某些可能出错的代码...
# 如果出错了,程序跳到 except,f.close() 被跳过
f.close()
# === 正确但啰嗦的写法3:try/finally ===
f = None
try:
    f = open("data.txt""r", encoding="utf-8")
    content = f.read()
# 假设这里可能出错
finally:
if f isnotNone:
        f.close()      # 无论如何都会执行,但写法很繁琐
# 每次都要写 try/finally,太啰嗦了!
1.3 有 with 的世界(优雅解法)
# 一行 with 搞定上面所有操作!
withopen("data.txt""r", encoding="utf-8"as f:
    content = f.read()
# 即使这里出错,文件也会自动关闭
# 出了 with 块,f 自动关闭,无需手动操作

with 做的事情:你只要告诉它"借什么",它自动帮你"还"。无论 with 块里的代码正常结束还是中途出错,资源都会被正确释放。

1.4 本教程学习路线
with 语句的原理(底层调了什么)
    │
    ├── 基于类实现上下文管理器(__enter__ + __exit__)
    │
    ├── __exit__ 异常处理机制(三个参数 + 返回值控制)
    │
    ├── contextlib 工具库
    │   ├── @contextmanager(用生成器快速实现)
    │   ├── closing()(把有 close 的对象变成上下文管理器)
    │   ├── suppress()(忽略指定异常)
    │   └── ContextDecorator(既是装饰器又是上下文管理器)
    │
    └── 综合实战案例(计时器、临时清理、异常日志)

二、with 语句的核心原理
2.1 with 语句的执行流程
withopen("data.txt""r"as f:
    content = f.read()

Python 解释器在背后做了这些事:

步骤1open("data.txt""r")  →  创建一个文件对象
步骤2:调用 文件对象.__enter__()  →  返回文件对象自身,赋值给 f
步骤3:执行 with 块内的代码  →  content = f.read()
步骤4:无论步骤3是否出错,调用 文件对象.__exit__(exc_type, exc_val, traceback)
       - 如果步骤3没出错:传入 (NoneNoneNone)
       - 如果步骤3出错了:传入异常的具体信息
2.2 等价代码(彻底理解 with 的底层)
# 这句代码:
# with 表达式 as 变量:
#     代码块
# 完全等价于:
管理器 = 表达式           # 例如:管理器 = open("data.txt")
变量 = 管理器.__enter__() # 例如:f = 文件对象.__enter__()
try:
    代码块               # 执行 with 块里的代码
except:
# 如果出错,让 __exit__ 决定怎么处理
ifnot 管理器.__exit__(异常类型, 异常值, traceback):
raise# __exit__ 返回 False → 重新抛出异常
else:
# 如果没出错
    管理器.__exit__(NoneNoneNone)
2.3 手动模拟 with 的底层过程
classMyOpen:
"""模拟文件打开的上下文管理器——帮助理解 with 原理"""
def__init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def__enter__(self):
"""进入上下文:打开文件"""
print(f"  [__enter__] 打开文件:{self.filename}")
self.file = open(self.filename, self.mode)
returnself.file        # 这个返回值赋给 as 后面的变量
def__exit__(self, exc_type, exc_val, traceback):
"""退出上下文:关闭文件"""
print(f"  [__exit__] 关闭文件:{self.filename}")
ifself.file:
self.file.close()
# 返回 False(或不返回)→ 异常继续向上抛
# === 演示:正常结束 ===
print("=== 正常情况 ===")
with MyOpen("test.txt""w"as f:
print("  [with块] 写入数据...")
    f.write("Hello")
# 输出:
# === 正常情况 ===
#   [__enter__] 打开文件:test.txt
#   [with块] 写入数据...
#   [__exit__] 关闭文件:test.txt
# === 演示:中途出错 ===
print("\n=== 出错情况 ===")
try:
with MyOpen("test.txt""r"as f:
print("  [with块] 读取中...")
raise ValueError("模拟一个错误!")  # 故意抛出异常
except ValueError:
print("  [外部] 捕获到了异常,但文件已经关闭了!")
# 输出:
# === 出错情况 ===
#   [__enter__] 打开文件:test.txt
#   [with块] 读取中...
#   [__exit__] 关闭文件:test.txt        ← 即使出错,文件也关闭了!
#   [外部] 捕获到了异常,但文件已经关闭了!

关键发现:即使在 with 块中抛出异常,__exit__ 仍然会被调用,资源仍然被正确释放。

2.4 with 语句的多种写法
# 写法1:单资源
withopen("a.txt"as f:
pass
# 写法2:多资源(逗号分隔)
withopen("a.txt"as f1, open("b.txt"as f2:
pass
# 写法3:不获取资源(纯控制)——某些上下文管理器 as 后面可以没有变量
# 例如:with lock:  或  with redirect_stdout(f):
# 写法4:多资源嵌套(Python 3.10+ 可用括号换行)
with (
open("input.txt"as f_in,
open("output.txt""w"as f_out,
):
    f_out.write(f_in.read())
2.5 with 能管理的对象
# 以下都是 Python 内置的上下文管理器,可以直接用 with
# ① 文件对象
withopen("file.txt"as f:
pass
# ② 线程锁
import threading
lock = threading.Lock()
with lock:                # 自动 acquire 和 release
# 临界区代码
pass
# ③ 数据库连接
import sqlite3
with sqlite3.connect("test.db"as conn:  # 自动 commit/rollback 和 close
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users")
# ④ 临时目录
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
# tmpdir 是临时目录的路径
# 退出 with 后自动删除整个目录
pass
# ⑤ 网络连接(如 urllib)
from urllib.request import urlopen
with urlopen("https://www.python.org"as response:
    html = response.read()

新手易踩坑:

坑 1:with 后面的表达式不能是任意对象 → 必须实现 __enter__ 和 __exit__ 方法
坑 2as f 只能获取 __enter__ 的返回值,不是 with 后面的表达式本身
坑 3:出了 with 块后,资源已释放,不要再使用 f(如 f.read()
课堂小练习 1

用 with 语句实现:打开一个文件写入 "Hello World",然后再次用 with 读出来并打印。对比用 try/finally 的写法,体会 with 的简洁。

点击查看参考答案
# with 写法
withopen("hello.txt""w", encoding="utf-8"as f:
    f.write("Hello World")
withopen("hello.txt""r", encoding="utf-8"as f:
print(f.read())  # Hello World
# 对比 try/finally 写法(同样功能但更啰嗦)
f = None
try:
    f = open("hello.txt""w", encoding="utf-8")
    f.write("Hello World")
finally:
if f:
        f.close()
f = None
try:
    f = open("hello.txt""r", encoding="utf-8")
print(f.read())
finally:
if f:
        f.close()

三、基于类的上下文管理器:enter 与 exit
3.1 协议定义

要实现一个上下文管理器,类必须定义两个方法:

方法
调用时机
参数
返回值
__enter__(self)
进入 with 块时
只有 self
赋给 as 后面的变量
__exit__(self, exc_type, exc_val, exc_tb)
退出 with 块时
异常信息(三个参数)
True
 抑制异常,False 继续抛出
┌────────────────────────────────────────────┐
│  with MyManager() as obj:                  │
│      # 1. MyManager() 创建实例             │
│      # 2. 调用实例.__enter__() → 返回 obj  │
│      # 3. 执行 with 块内的代码             │
│      # 4. 调用实例.__exit__(...)           │
│      #    └─ 没出错 → (None, None, None)   │
│      #    └─ 出错了 → (异常类型, 异常值,   │
│      #                   traceback)        │
└────────────────────────────────────────────┘
3.2 最简实现:文件操作的上下文管理器
classManagedFile:
"""文件操作的上下文管理器 —— 自动关闭文件"""
def__init__(self, filename, mode="r", encoding="utf-8"):
"""初始化:记录文件名和模式,但不打开文件"""
self.filename = filename
self.mode = mode
self.encoding = encoding
self.file = None# 文件句柄,初始为 None
def__enter__(self):
"""进入上下文:打开文件,返回文件对象"""
print(f"[打开] {self.filename}")
self.file = open(self.filename, self.mode, encoding=self.encoding)
returnself.file        # 这个值赋给 as 后面的变量
def__exit__(self, exc_type, exc_val, exc_tb):
"""退出上下文:关闭文件(无论是否发生异常)"""
ifself.file:
self.file.close()
print(f"[关闭] {self.filename}")
# 返回 False(或不返回):异常继续向上传播
# 返回 True:吞掉异常,外部不会感知到
returnFalse
# === 使用 ===
with ManagedFile("greeting.txt""w"as f:
    f.write("你好,上下文管理器!\n")
    f.write("第二行内容\n")
# 输出:
# [打开] greeting.txt
# [关闭] greeting.txt
# 验证写入的内容
with ManagedFile("greeting.txt""r"as f:
    content = f.read()
print(content)
# 输出:
# [打开] greeting.txt
# [关闭] greeting.txt
# 你好,上下文管理器!
# 第二行内容
3.3 复杂实现:数据库连接的上下文管理器
import sqlite3
classDatabaseConnection:
"""数据库连接的上下文管理器 —— 自动 commit/rollback 和关闭"""
def__init__(self, db_path):
"""初始化:记录数据库路径"""
self.db_path = db_path
self.connection = None
self.cursor = None
def__enter__(self):
"""进入上下文:建立连接,返回游标"""
print(f"[数据库] 连接 {self.db_path}")
self.connection = sqlite3.connect(self.db_path)
self.cursor = self.connection.cursor()
# 创建示例表(如果不存在)
self.cursor.execute("""
            CREATE TABLE IF NOT EXISTS students (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                score REAL
            )
        """)
returnself.cursor      # 返回游标,方便调用者直接执行 SQL
def__exit__(self, exc_type, exc_val, exc_tb):
"""退出上下文:根据是否有异常决定 commit 还是 rollback"""
if exc_type isNone:
# 没有异常 → 提交事务
self.connection.commit()
print(f"[数据库] 提交事务 ✓")
else:
# 有异常 → 回滚事务
self.connection.rollback()
print(f"[数据库] 回滚事务 ✗ (原因: {exc_type.__name__}{exc_val})")
# 无论如何都要关闭连接
ifself.cursor:
self.cursor.close()
ifself.connection:
self.connection.close()
print(f"[数据库] 连接已关闭")
returnFalse# 异常继续向上传播
# === 使用:正常情况 ===
print("=== 正常操作 ===")
with DatabaseConnection("school.db"as db:
    db.execute("INSERT INTO students (name, score) VALUES (?, ?)", ("小明"95))
    db.execute("INSERT INTO students (name, score) VALUES (?, ?)", ("小红"88))
# 输出:
# === 正常操作 ===
# [数据库] 连接 school.db
# [数据库] 提交事务 ✓
# [数据库] 连接已关闭
# === 使用:异常情况 ===
print("\n=== 异常操作 ===")
try:
with DatabaseConnection("school.db"as db:
        db.execute("INSERT INTO students (name, score) VALUES (?, ?)", ("小刚"75))
raise ValueError("模拟数据库写入后发生的错误!")
except ValueError as e:
print(f"外部捕获到异常:{e}")
# 输出:
# === 异常操作 ===
# [数据库] 连接 school.db
# [数据库] 回滚事务 ✗ (原因: ValueError: 模拟数据库写入后发生的错误!)
# [数据库] 连接已关闭
# 外部捕获到异常:模拟数据库写入后发生的错误!
# === 验证数据 ===
print("\n=== 验证数据 ===")
with DatabaseConnection("school.db"as db:
    result = db.execute("SELECT * FROM students").fetchall()
for row in result:
print(f"  {row}")
# 只有小明和小红(正常提交的),小刚的记录已回滚
3.4 enter 返回 self 的用法
classCountdown:
"""倒计时上下文管理器 —— __enter__ 返回自身"""
def__init__(self, start):
self.start = start
self.current = start
def__enter__(self):
print(f"倒计时开始:{self.start}")
returnself# 返回自身,调用方可以通过 as 拿到实例
def__exit__(self, exc_type, exc_val, exc_tb):
print(f"倒计时结束(已数到 {self.current})")
returnFalse
deftick(self):
"""手动推进倒计时"""
ifself.current > 0:
print(f"  {self.current}...")
self.current -= 1
returnTrue
print("  时间到!")
returnFalse
# 使用:as 拿到的是 Countdown 实例本身
with Countdown(3as cd:
    cd.tick()   # 3...
    cd.tick()   # 2...
    cd.tick()   # 1...
# 输出:
# 倒计时开始:3
#   3...
#   2...
#   1...
# 倒计时结束(已数到 0)

新手易踩坑:

坑 1__exit__ 方法忘了写第三个参数 → 参数数量不匹配,调用时报 TypeError
坑 2__exit__ 中 return False 写成了 return → return 等同于 return NoneNone 在布尔上下文中是 False,所以效果一样,但不够明确
坑 3:在 __enter__ 里初始化了资源但在 __exit__ 里没有释放 → 资源泄漏,背离上下文管理器的设计初衷
课堂小练习 2

自己写一个 ReversibleList 上下文管理器:进入时打印列表内容,退出时自动将列表反转。__enter__ 返回自身。

# 期望效果:
with ReversibleList([12345]) as rl:
print("处理数据...")
# 输出:
# 进入:[1, 2, 3, 4, 5]
# 处理数据...
# 退出(已反转):[5, 4, 3, 2, 1]
点击查看参考答案
classReversibleList:
def__init__(self, data):
self.data = data
def__enter__(self):
print(f"进入:{self.data}")
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
self.data.reverse()
print(f"退出(已反转):{self.data}")
returnFalse
with ReversibleList([12345]) as rl:
print("处理数据...")

四、exit的三个异常参数详解
4.1 三个参数的含义

__exit__(self, exc_type, exc_val, exc_tb) 的三个参数:

参数
含义
with 块没出错时
with 块出错时
exc_type
异常的类型
None
例如 ValueError
exc_val
异常的实例(异常值)
None
例如 ValueError("负数")
exc_tb
Traceback 对象(调用栈)
None
完整的 traceback 信息
4.2 返回值控制异常传播
classExceptionDemo:
"""演示 __exit__ 返回值对异常传播的影响"""
def__enter__(self):
print("[进入]")
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
print(f"[退出] exc_type={exc_type}, exc_val={exc_val}")
# === 情况1:返回 False → 异常继续向上传播(默认行为) ===
# return False
# === 情况2:返回 True → 吞掉异常,外部感知不到 ===
returnTrue
# 测试:__exit__ 返回 True 会吞掉异常
print("=== 测试:异常被吞掉 ===")
with ExceptionDemo():
print("  with 块执行中...")
raise ValueError("一个错误!")
print("这行竟然被执行了!因为异常被 __exit__ 吞掉了。")
# 输出:
# === 测试:异常被吞掉 ===
# [进入]
#   with 块执行中...
# [退出] exc_type=<class 'ValueError'>, exc_val=一个错误!
# 这行竟然被执行了!因为异常被 __exit__ 吞掉了。
4.3 实战:选择性吞掉某些异常
classSuppressKeyError:
"""只吞掉 KeyError,其他异常正常抛出"""
def__enter__(self):
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type is KeyError:
print(f"[已抑制] KeyError: {exc_val}")
returnTrue# 吞掉 KeyError
# 其他异常 → 返回 False,继续向上抛
returnFalse
# 测试
print("=== 测试1:KeyError 被吞掉 ===")
with SuppressKeyError():
    data = {"name""小明"}
print(data["age"])     # 触发 KeyError —— 被吞掉了
print("程序继续运行!\n")
print("=== 测试2:ValueError 正常抛出 ===")
try:
with SuppressKeyError():
raise ValueError("其他错误")  # ValueError —— 不会被吞
except ValueError as e:
print(f"外部捕获:{e}")
# 输出:
# === 测试1:KeyError 被吞掉 ===
# [已抑制] KeyError: 'age'
# 程序继续运行!
#
# === 测试2:ValueError 正常抛出 ===
# 外部捕获:其他错误
4.4 exit 中访问异常信息
import traceback
classErrorLogger:
"""记录异常详细信息的上下文管理器"""
def__init__(self, log_file="error.log"):
self.log_file = log_file
def__enter__(self):
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type isnotNone:
# 发生了异常,记录详细信息
withopen(self.log_file, "a", encoding="utf-8"as f:
                f.write(f"[异常类型] {exc_type.__name__}\n")
                f.write(f"[异常信息] {exc_val}\n")
                f.write(f"[调用栈]\n")
# traceback.format_tb() 获取格式化的调用栈
                traceback.print_tb(exc_tb, file=f)
                f.write("-" * 40 + "\n")
print(f"异常已记录到 {self.log_file}")
returnFalse# 异常继续传播,不吞掉
# 使用
defcalculate(a, b):
return a / b
defprocess():
with ErrorLogger():
        calculate(100)   # 故意除以零
try:
    process()
except ZeroDivisionError:
print("外部捕获了 ZeroDivisionError")
# error.log 文件内容:
# [异常类型] ZeroDivisionError
# [异常信息] division by zero
# [调用栈]
#   File "xxx.py", line xx, in process
#     calculate(10, 0)
#   File "xxx.py", line xx, in calculate
#     return a / b
# ----------------------------------------
4.5 exit 参数总结图解
with 块中的代码执行...
    ┌─── 没出错 ───→ __exit__(NoneNoneNone)
    │                    │
    │                    └──→ 正常退出
    │
    └─── 出错了 ───→ __exit__(ValueError, ValueError("msg"), traceback)
                         │
                         ├──→ returnTrue  →  吞掉异常,外部无感知
                         │
                         └──→ returnFalse →  异常继续向上传播到外部

新手易踩坑:

坑 1__exit__ 返回 True 吞掉了所有异常 → 调试时完全不知道哪里出错了!
坑 2:在 __exit__ 里重新抛出异常(raise)→ 会覆盖原始异常,让调试变得更困难
坑 3__exit__ 中的代码本身可能抛异常 → 如果在关闭资源时出错,Python 会优先抛出 __exit__ 中的异常,原始的异常信息可能丢失
课堂小练习 3

写一个 LogIfError 上下文管理器:只有当 with 块发生异常时,才将异常类型和异常信息打印出来。异常照常传播,不吞掉。

点击查看参考答案
classLogIfError:
def__enter__(self):
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type isnotNone:
print(f"[异常日志] {exc_type.__name__}{exc_val}")
returnFalse# 不吞异常
# 测试
with LogIfError():
print("正常操作,不会有日志")
# (无日志输出)
with LogIfError():
raise RuntimeError("测试异常")
# [异常日志] RuntimeError: 测试异常
# 异常继续传播...

五、contextlib 标准库工具

contextlib 模块提供了更便捷的方式来创建和使用上下文管理器,不需要每次都写一个完整的类。

5.1 @contextmanager:用生成器实现上下文管理器

这是最常用的工具——用一个生成器函数就能创建上下文管理器,代码量减少 80%。

from contextlib import contextmanager
@contextmanager
defmanaged_file(filename, mode="r", encoding="utf-8"):
"""用生成器实现的文件上下文管理器 —— 无需写类!"""
print(f"[打开] {filename}")
    f = open(filename, mode, encoding=encoding)  # __enter__ 的逻辑
try:
yield f                                   # 返回资源给 with 块
finally:
        f.close()                                 # __exit__ 的逻辑
print(f"[关闭] {filename}")
# 用法和类实现的完全一样!
with managed_file("test.txt""w"as f:
    f.write("用生成器实现的上下文管理器!")
with managed_file("test.txt""r"as f:
print(f.read())

@contextmanager 的工作原理:

@contextmanager 装饰的生成器函数:
yield 之前的代码  →  等价于 __enter__
          │
yield 后面的值    →  等价于 __enter__ 的返回值(赋给 as
          │
yield 之后的代码  →  等价于 __exit__(放在 finally 中确保执行)
执行流程:
┌─────────────────────────────────┐
│  生成器开始执行                   │
│  ├── yield 之前的代码(准备资源)  │ ← __enter__
│  ├── yield 资源 → 交给 with 块   │
│  │   (函数在此暂停)               │
│  ├── with 块执行...              │
│  ├── with 块结束                 │
│  ├── yield 之后的代码(清理资源)  │ ← __exit__
│  └── 生成器结束                   │
└─────────────────────────────────┘
5.2 @contextmanager 的异常处理
from contextlib import contextmanager
@contextmanager
defsafe_operation(name):
"""演示 @contextmanager 中的异常处理"""
print(f"[开始] {name}")
try:
yieldf"资源-{name}"# 正常返回资源
except ValueError as e:
# 如果捕获了异常,就不会向上传播(相当于 __exit__ 返回 True)
print(f"[捕获] 在上下文管理器内部处理了:{e}")
# 不重新 raise → 异常被吞掉
except Exception:
# 其他异常 → 在 finally 之前重新抛出
print("[发现] 其他类型异常,重新抛出")
raise
finally:
# finally 块中的代码无论异常与否都会执行
print(f"[结束] {name}")
# 测试1:正常情况
print("=== 测试1:正常 ===")
with safe_operation("测试1"as res:
print(f"  使用 {res}")
# 输出:
# === 测试1:正常 ===
# [开始] 测试1
#   使用 资源-测试1
# [结束] 测试1
# 测试2:ValueError 被吞掉
print("\n=== 测试2:ValueError ===")
with safe_operation("测试2"as res:
print(f"  使用 {res}")
raise ValueError("值错误")
print("ValueError 被吞掉了,程序继续!")
# 测试3:其他异常正常传播
print("\n=== 测试3:RuntimeError ===")
try:
with safe_operation("测试3"as res:
raise RuntimeError("运行时错误")
except RuntimeError as e:
print(f"外部捕获:{e}")
# 输出:
# [开始] 测试3
# [发现] 其他类型异常,重新抛出
# [结束] 测试3
# 外部捕获:运行时错误
5.3 常见错误:忘了 try/finally
from contextlib import contextmanager
# 错误写法:没有 try/finally
@contextmanager
defbroken_manager(filename):
    f = open(filename, "w")
yield f
    f.close()  # 如果 with 块中出现异常,这行不会执行!
# 正确写法:必须用 try/finally
@contextmanager
defcorrect_manager(filename):
    f = open(filename, "w")
try:
yield f
finally:
        f.close()  # 无论如何都会执行

新手易踩坑:

坑 1@contextmanager 里 yield 后面的代码没用 try/finally 包裹 → 出错时资源不会释放
坑 2@contextmanager 里 yield 只能出现一次 → 生成器只能 yield 一个值
坑 3yield 返回的值赋给了 as 后面的变量,yield 本身没有值
5.4 closing():给有 close() 的对象"披上"上下文管理器外衣

很多第三方库的对象有 close() 方法但没有实现 __enter__/__exit__closing() 就是为这些对象服务的。

from contextlib import closing
from urllib.request import urlopen
# urlopen 返回的对象有 close() 但没有实现上下文管理协议(旧版本)
# closing() 帮它"补上"这个能力
# 不推荐(旧式写法):
response = urlopen("https://httpbin.org/get")
try:
    html = response.read()
finally:
    response.close()
# 推荐(用 closing 包装):
with closing(urlopen("https://httpbin.org/get")) as response:
    html = response.read()
# 退出 with 时自动调用 response.close()
# closing 的本质(源码级简化版):
# class closing:
#     def __init__(self, thing):
#         self.thing = thing
#     def __enter__(self):
#         return self.thing
#     def __exit__(self, *args):
#         self.thing.close()
5.5 suppress():优雅地忽略指定异常
from contextlib import suppress
# 传统写法:用 try/except 忽略 KeyError
data = {"name""小明"}
try:
print(data["age"])
except KeyError:
pass# 吞掉异常,什么都不做
# suppress 写法:更简洁
data = {"name""小明"}
with suppress(KeyError):
print(data["age"])  # KeyError 被吞掉,程序继续
# 实战:安全地删除文件(文件不存在不报错)
import os
from contextlib import suppress
with suppress(FileNotFoundError):
    os.remove("maybe_not_exist.txt")
# 实战:安全地关闭多个网络连接
with suppress(OSError):
    socket1.close()
with suppress(OSError):
    socket2.close()
# 可以传入多个异常类型
with suppress(KeyError, IndexError, TypeError):
    value = some_risky_operation()
5.6 ContextDecorator:既是上下文管理器又是装饰器
from contextlib import ContextDecorator
classlog_execution(ContextDecorator):
"""既可以当上下文管理器用,也可以当装饰器用!"""
def__init__(self, prefix=""):
self.prefix = prefix
def__enter__(self):
print(f"{self.prefix}[进入]")
returnself
def__exit__(self, *args):
print(f"{self.prefix}[退出]")
# 用法1:上下文管理器
with log_execution("[CM] "):
print("  执行操作...")
# 输出:
# [CM] [进入]
#   执行操作...
# [CM] [退出]
# 用法2:装饰器(整个函数自动包裹在 with 中!)
@log_execution("[装饰] ")
defmy_function():
print("  函数内容")
my_function()
# 输出:
# [装饰] [进入]
#   函数内容
# [装饰] [退出]
5.7 redirect_stdout / redirect_stderr:重定向标准输出
from contextlib import redirect_stdout
import io
# 场景:捕获 print 的输出,而不是让它打印到控制台
f = io.StringIO()                    # 一个内存中的"文件"
with redirect_stdout(f):             # 把 print 的输出重定向到 f
print("这行不会显示在控制台")
print("这行也不会")
print("所有的 print 都进了 StringIO")
output = f.getvalue()                # 获取被截获的输出
print("=== 截获的输出 ===")
print(output)
# 输出:
# === 截获的输出 ===
# 这行不会显示在控制台
# 这行也不会
# 所有的 print 都进了 StringIO
5.8 contextlib 工具速查表
工具
作用
典型场景
@contextmanager
用生成器快速创建上下文管理器
不需要写完整类时
closing(thing)
给有 close() 的对象加上下文管理
第三方库对象
suppress(*exceptions)
忽略指定异常
文件删除、资源关闭
ContextDecorator
上下文管理 + 装饰器二合一
需要两种用法的工具
redirect_stdout(f)
重定向标准输出
捕获 print 输出
redirect_stderr(f)
重定向标准错误
捕获错误输出
ExitStack
动态管理多个上下文管理器
不确定数量的资源管理(进阶)
课堂小练习 4

用 @contextmanager 实现一个 timer 上下文管理器,统计 with 块中代码的运行时间(秒)。

点击查看参考答案
from contextlib import contextmanager
import time
@contextmanager
deftimer(name="代码块"):
    start = time.perf_counter()
try:
yield
finally:
        elapsed = time.perf_counter() - start
print(f"[{name}] 耗时:{elapsed:.4f} 秒")
with timer("计算测试"):
    total = sum(range(10000000))
# 输出:[计算测试] 耗时:0.2158 秒

六、综合实战案例
案例 1:代码计时上下文管理器(完整版)

需求:统计 with 块内代码的执行时间,支持命名、支持获取详细统计。

import time
from contextlib import contextmanager
classTimer:
"""高性能计时器 —— 统计代码块运行时间"""
def__init__(self, name="代码块"):
self.name = name
self.start_time = None
self.end_time = None
self.elapsed = None
def__enter__(self):
# 使用 perf_counter 而非 time.time(精度更高,不受系统时间调整影响)
self.start_time = time.perf_counter()
returnself# 返回自身,外部可以获取详细数据
def__exit__(self, *args):
self.end_time = time.perf_counter()
self.elapsed = self.end_time - self.start_time
# 格式化输出(自动选择合适的单位)
ifself.elapsed < 0.001:
print(f"[Timer] {self.name}{self.elapsed * 1_000_000:.2f} μs")
elifself.elapsed < 1:
print(f"[Timer] {self.name}{self.elapsed * 1000:.2f} ms")
else:
print(f"[Timer] {self.name}{self.elapsed:.4f} s")
returnFalse# 不吞异常
# === 使用演示 ===
with Timer("大数据排序"as t:
    data = list(range(100000000, -1))
    data.sort()
# 可以事后查看 t.elapsed 属性
# [Timer] 大数据排序: 0.3241 s
with Timer("快速操作"):
    result = sum(range(1000))
# [Timer] 快速操作: 0.12 ms
案例 2:临时文件自动清理上下文管理器

需求:在 with 块内创建一个临时文件(或目录),退出时自动删除,无论代码是否出错。

import os
import tempfile
from pathlib import Path
classTemporaryWorkspace:
"""临时工作目录 —— 退出时自动清理所有内容"""
def__init__(self, prefix="workspace_", keep_on_error=False):
"""
        prefix: 临时目录名前缀
        keep_on_error: 如果 with 块出错,是否保留目录(调试用)
        """
self.prefix = prefix
self.keep_on_error = keep_on_error
self.path = None
def__enter__(self):
# 创建临时目录
self.path = Path(tempfile.mkdtemp(prefix=self.prefix))
print(f"[创建] 临时目录:{self.path}")
returnself.path
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type isnotNoneandself.keep_on_error:
# 出错了但不删除——方便事后查看中间产物
print(f"[保留] 因出错保留目录:{self.path}")
returnFalse
# 递归删除整个目录及其所有内容
import shutil
        shutil.rmtree(self.path, ignore_errors=True)
print(f"[清理] 临时目录已删除:{self.path}")
returnFalse
# === 使用演示 ===
# 场景1:正常使用
with TemporaryWorkspace("data_"as ws:
# 在临时目录中创建文件
    (ws / "input.txt").write_text("临时数据", encoding="utf-8")
    (ws / "output.csv").write_text("列1,列2\n1,2", encoding="utf-8")
# 列出临时目录内容
print("临时目录中的文件:")
for f in ws.iterdir():
print(f"  - {f.name}")
# 输出:
# [创建] 临时目录:C:\Users\...\Temp\data_xxxxxx
# 临时目录中的文件:
#   - input.txt
#   - output.csv
# [清理] 临时目录已删除:C:\Users\...\Temp\data_xxxxxx
# 场景2:出错时保留现场
with TemporaryWorkspace("debug_", keep_on_error=Trueas ws:
    (ws / "partial_result.txt").write_text("部分数据", encoding="utf-8")
raise RuntimeError("处理过程中出错!")
# 输出:
# [创建] 临时目录:C:\Users\...\Temp\debug_xxxxxx
# [保留] 因出错保留目录:C:\Users\...\Temp\debug_xxxxxx
# (RuntimeError 继续传播)
案例 3:异常捕获日志记录上下文管理器

需求:包裹一段代码,捕获指定类型的异常,记录日志,支持重试。

import time
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
    level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
classErrorMonitor:
"""
    异常监控上下文管理器
    功能:
    1. 捕获指定类型的异常并记录日志
    2. 支持自动重试(retry 次)
    3. 记录每次重试的详细信息
    """
def__init__(self, operation_name, retry=0, delay=1,
                 catch=(Exception,), reraise=True):
"""
        operation_name: 操作名称(用于日志)
        retry: 失败后重试次数
        delay: 重试间隔(秒)
        catch: 要捕获的异常类型(元组)
        reraise: 重试耗尽后是否重新抛出异常
        """
self.name = operation_name
self.retry = retry
self.delay = delay
self.catch = catch
self.reraise = reraise
self.attempt = 0
self.final_error = None
def__enter__(self):
print(f"\n{'='*50}")
print(f"[操作] {self.name}")
print(f"[时间] {datetime.now().strftime('%H:%M:%S')}")
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type isNone:
print(f"[结果] 操作成功 ✓")
print(f"{'='*50}\n")
returnFalse
# 判断是否是我们要捕获的异常类型
ifissubclass(exc_type, self.catch):
self.attempt += 1
print(f"[失败] 第 {self.attempt} 次尝试:{exc_type.__name__}{exc_val}")
# 还有重试次数 → 吞掉异常,让外部代码进入下一次尝试
ifself.attempt <= self.retry:
print(f"[重试] 等待 {self.delay} 秒后重试...")
                time.sleep(self.delay)
returnTrue# 吞掉异常,外部循环继续
# 重试耗尽
self.final_error = exc_val
print(f"[放弃] 已重试 {self.retry} 次,仍然失败")
print(f"{'='*50}\n")
ifself.reraise:
returnFalse# 让异常继续向上传播
else:
returnTrue# 彻底吞掉
# 不是我们要捕获的类型 → 照常传播
print(f"[不可处理] {exc_type.__name__}(不在捕获范围内)")
print(f"{'='*50}\n")
returnFalse
# === 使用演示 ===
import random
defflaky_operation():
"""模拟不稳定的操作(40% 成功率)"""
if random.random() < 0.4:
return"成功"
raise ConnectionError("网络连接超时")
# 最多重试 3 次,每次间隔 1 秒
for _ inrange(5):  # 外层最多尝试 5 次
with ErrorMonitor("网络数据获取", retry=3, delay=1,
                      catch=(ConnectionError, TimeoutError)) as monitor:
        result = flaky_operation()
print(f"[数据] {result}")
break# 成功就退出循环
else:
print("最终放弃:操作仍然失败")

运行效果演示:

==================================================
[操作] 网络数据获取
[时间] 14:30:00
[失败] 第 1 次尝试:ConnectionError: 网络连接超时
[重试] 等待 1 秒后重试...
==================================================
[操作] 网络数据获取
[时间] 14:30:01
[失败] 第 2 次尝试:ConnectionError: 网络连接超时
[重试] 等待 1 秒后重试...
==================================================
[操作] 网络数据获取
[时间] 14:30:02
[数据] 成功
[结果] 操作成功 ✓
==================================================
案例 4:组合多个上下文管理器的高级用法
from contextlib import contextmanager, redirect_stdout
import io
import time
@contextmanager
defmeasure_and_capture(name="操作"):
"""
    组合上下文管理器:
    1. 计时(Timer 逻辑)
    2. 捕获 print 输出
    """
    f = io.StringIO()
    start = time.perf_counter()
try:
# 嵌套两个上下文:redirect_stdout 在内部
with redirect_stdout(f):
yield f          # 把 StringIO 传给 with 块
finally:
        elapsed = time.perf_counter() - start
        output = f.getvalue()
print(f"[{name}] 耗时 {elapsed:.4f}s")
if output.strip():
print(f"[{name}] 产生的输出:")
for line in output.strip().split("\n"):
print(f"  │ {line}")
# 使用
with measure_and_capture("数据处理"as captured:
print("步骤1:加载数据...")
    total = sum(range(1000000))
print(f"步骤2:计算结果 = {total}")
# 输出:
# [数据处理] 耗时 0.0412s
# [数据处理] 产生的输出:
#   │ 步骤1:加载数据...
#   │ 步骤2:计算结果 = 499999500000
课堂小练习 5

实现一个 Transaction 上下文管理器,模拟银行转账的事务处理:

进入时打印"开始事务"
用 __exit__ 的参数判断是否有异常
无异常时打印"提交事务",有异常时打印"回滚事务" + 异常原因
异常照常传播
点击查看参考答案
classTransaction:
def__init__(self, name="事务"):
self.name = name
def__enter__(self):
print(f"[{self.name}] 开始")
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
if exc_type isNone:
print(f"[{self.name}] 提交 ✓")
else:
print(f"[{self.name}] 回滚 ✗ ({exc_type.__name__}{exc_val})")
returnFalse# 异常继续传播
# 正常情况
with Transaction("转账"):
print("  张三 -100")
print("  李四 +100")
# [转账] 开始
#   张三 -100
#   李四 +100
# [转账] 提交 ✓
# 异常情况
with Transaction("转账"):
print("  张三 -100")
raise ValueError("李四账户不存在!")
# [转账] 开始
#   张三 -100
# [转账] 回滚 ✗ (ValueError: 李四账户不存在!)

七、常见误区与面试题
7.1 易错点汇总

错误 1:把 with 当成"作用域"来限制变量访问

# 错误理解:以为出了 with 就不能用 f 了
withopen("test.txt"as f:
    content = f.read()
# f 仍然存在!只是 f.close() 已经被调用了
print(f.closed)  # True —— 文件已关闭
# f.read()      # ValueError: I/O operation on closed file.
# 如果调用 f.read(),会报错——不是变量不存在,而是文件已关闭

错误 2:在 @contextmanager 中忘了 try/finally

# 错误:yield 后的代码不在 finally 中
@contextmanager
defbad_manager(filename):
    f = open(filename)
yield f
    f.close()  # 如果 yield 处抛出异常,这行不会执行!
# 正确:
@contextmanager
defgood_manager(filename):
    f = open(filename)
try:
yield f
finally:
        f.close()  # 无论如何都会执行

错误 3:exit 吞掉异常但不做任何处理

classDangerousManager:
def__enter__(self):
returnself
def__exit__(self, exc_type, exc_val, exc_tb):
returnTrue# 吞掉所有异常!调试时完全不知道问题在哪
# 正确:除非你清楚自己在做什么,否则 return False

错误 4:混淆 with 和 try/except 的作用

# with 的作用是管理资源(自动释放),不是捕获异常
# try/except 的作用才是捕获异常
# 错误期望:以为 with 会捕获 FileNotFoundError
withopen("not_exist.txt"as f:  # FileNotFoundError 仍然会抛出!
pass
# 需要自己加 try/except
# 正确:
try:
withopen("not_exist.txt"as f:
pass
except FileNotFoundError:
print("文件不存在")

错误 5:在 enter 里分配了资源但在 exit 出错时不处理

classBadConnection:
def__enter__(self):
self.resource1 = acquire_resource1()  # 分配成功
self.resource2 = acquire_resource2()  # 如果这里出错...
returnself
# 问题:如果 resource2 分配失败,resource1 不会被释放!
# 正确做法:分步处理
classGoodConnection:
def__enter__(self):
self.resource1 = acquire_resource1()
try:
self.resource2 = acquire_resource2()
except:
self.resource1.close()  # 失败时手动释放 resource1
raise
returnself
记忆图谱
上下文管理器知识地图:
with 语句
                   (自动管理资源)
                        │
          ┌─────────────┼─────────────┐
          ↓             ↓             ↓
    __enter__     with 代码块     __exit__
   (获取资源)   (使用资源)   (释放资源)
          │                           │
          │              ┌────────────┤
          │              ↓            ↓
          │       没出错→(None,...)  出错了→(异常信息)
          │              │            │
          │              │     returnTrue  吞掉
          │              │     returnFalse 传播
          │              │
          └────── 返回值→ as 变量

核心心法三句话:

1.
with = 自动借还:你只管用,__exit__ 帮你自动归还
2.
@contextmanager = 简化版:用生成器替代类,yield 前 = __enter__,yield 后 = __exit__
3.
**exit 返回 True 吞异常,False 传异常**:正常情况下用 False,除非你明确知道要吞掉

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 22:52:09 HTTP/2.0 GET : https://f.mffb.com.cn/a/506807.html
  2. 运行时间 : 0.165800s [ 吞吐率:6.03req/s ] 内存消耗:4,512.13kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=b5553ed644050abcc32027bd34d469d3
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000831s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000734s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.010231s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000241s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000548s ]
  6. SELECT * FROM `set` [ RunTime:0.001605s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000643s ]
  8. SELECT * FROM `article` WHERE `id` = 506807 LIMIT 1 [ RunTime:0.000665s ]
  9. UPDATE `article` SET `lasttime` = 1787323929 WHERE `id` = 506807 [ RunTime:0.003973s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000242s ]
  11. SELECT * FROM `article` WHERE `id` < 506807 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000435s ]
  12. SELECT * FROM `article` WHERE `id` > 506807 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001308s ]
  13. SELECT * FROM `article` WHERE `id` < 506807 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001585s ]
  14. SELECT * FROM `article` WHERE `id` < 506807 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000840s ]
  15. SELECT * FROM `article` WHERE `id` < 506807 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000736s ]
0.167365s