查看: 121|回复: 2

Python文件读写与with上下文管理器:从基础到自定义实现

[复制链接]
发表于 4 小时前 | 显示全部楼层 |阅读模式
文件读写是Python日常开发中最常见的I/O操作之一,包括日志处理、配置文件解析、接口数据落地等场景。Python内置的open()函数配合with语句,能安全高效地管理文件资源,避免资源泄露。本文将从基础用法到with的底层机制,结合代码示例进行完整梳理。

基础操作:open / read / write

Python通过open()打开文件,返回文件对象。基本参数包括模式('r'只读,'w'写入覆盖,'a'追加,'rb'/'wb'二进制)和编码(推荐encoding='utf-8')。使用后必须调用close()释放资源。
  1. f = open("test.txt", "r", encoding="utf-8")
  2. content = f.read()
  3. print(content)
  4. f.close()
复制代码

写文件示例:
  1. f = open("output.txt", "w", encoding="utf-8")
  2. f.write("Hello, Python I/O")
  3. f.close()
复制代码

注意:忘记close()会导致资源未释放;'w'模式会清空原有文件内容。

文本文件处理与逐行读取

处理文本时更常用逐行循环:
  1. f = open("test.txt", "r", encoding="utf-8")
  2. for line in f:
  3.     print(line.strip())
  4. f.close()
复制代码

逐行读取比read()一次性加载所有内容更适合大文件。

使用with语句(上下文管理器)简化文件操作

with语句自动管理文件资源,代码块结束后自动关闭文件,即使中间发生异常也能保证释放:
  1. with open("test.txt", "r", encoding="utf-8") as f:
  2.     content = f.read()
  3.     print(content)
复制代码

这等价于:打开文件 -> 使用文件 -> 自动调用close()。推荐作为标准写法。

处理大文件(逐行读取)

几百MB的文件不能一次性read()进内存,推荐逐行读取:
  1. with open("big_file.txt", "r", encoding="utf-8") as f:
  2.     for line in f:
  3.         process(line)
复制代码

更细粒度的控制:
  1. with open("big_file.txt", "r", encoding="utf-8") as f:
  2.     while True:
  3.         line = f.readline()
  4.         if not line:
  5.             break
  6.         process(line)
复制代码

这种方式只保留当前行在内存中,适用于日志分析、数据清洗等场景。

JSON文件读写(高频场景)

接口返回值、配置文件、爬虫结果常用JSON格式。Python内置json模块直接读写:

读取JSON:
  1. import json
  2. with open("data.json", "r", encoding="utf-8") as f:
  3.     data = json.load(f)
  4. print(data)
复制代码

写入JSON:
  1. import json
  2. data = {"name": "Alice", "age": 18}
  3. with open("data.json", "w", encoding="utf-8") as f:
  4.     json.dump(data, f, ensure_ascii=False, indent=2)
复制代码

参数说明:ensure_ascii=False避免中文被转义;indent=2美化输出。

with的本质:上下文管理器

with不仅用于文件,其核心是上下文管理机制(Context Management)。依赖的对象需实现两个魔术方法:__enter__()和__exit__()。

执行流程:
  1. with obj as x:
  2.     # 代码块
复制代码

等价于:
  1. x = obj.__enter__()
  2. try:
  3.     # 代码块
  4. finally:
  5.     obj.__exit__()
复制代码

__enter__()进入时做资源初始化,__exit__()退出时做资源释放。

with的其他常见用法

1. 线程锁
  1. import threading
  2. lock = threading.Lock()
  3. with lock:
  4.     # 临界区代码
  5.     print("线程安全操作")
复制代码

作用:自动加锁(__enter__)和自动释放锁(__exit__),避免忘记release()导致死锁。

2. 数据库连接/事务管理
很多数据库库支持with自动提交或回滚事务:
  1. with connection:
  2.     cursor.execute("INSERT INTO table VALUES (1)")
复制代码

或自动关闭游标:
  1. with connection.cursor() as cursor:
  2.     cursor.execute("SELECT * FROM table")
复制代码

3. 临时修改环境(上下文切换)
  1. from decimal import localcontext, Decimal
  2. with localcontext() as ctx:
  3.     ctx.prec = 2
  4.     print(Decimal("1.12345") + Decimal("2.34567"))
  5. print(Decimal("1.12345") + Decimal("2.34567"))
复制代码

在with内修改配置,退出后自动恢复原状态。

自定义上下文管理器

可以自己定义类实现__enter__和__exit__:
  1. class Timer:
  2.     def __enter__(self):
  3.         import time
  4.         self.start = time.time()
  5.         return self
  6.     def __exit__(self, exc_type, exc_val, exc_tb):
  7.         import time
  8.         print("耗时:", time.time() - self.start)
  9. with Timer():
  10.     sum(range(1000000))
复制代码

可用于统计耗时、日志记录、资源管理等。

更优雅的写法:contextlib

Python的contextlib模块提供@contextmanager装饰器,减少代码量:
  1. from contextlib import contextmanager
  2. import time
  3. @contextmanager
  4. def timer():
  5.     start = time.time()
  6.     yield
  7.     print("耗时:", time.time() - start)
  8. with timer():
  9.     sum(range(1000000))
复制代码

相比写类更直观简洁。

总结

with的核心是让“必须成对出现的操作”(获取/释放、开始/结束)变得安全且简洁。常见组合:
- 打开/关闭(文件)
- 加锁/解锁(线程)
- 开始/提交/回滚(数据库)
- 设置/恢复(环境)

熟练掌握with及其上下文管理机制,可以在日志处理、数据落地、接口调试等场景中直接应用,提升代码健壮性。
回复

使用道具 举报

发表于 4 小时前 | 显示全部楼层

Re: Python文件读写与with上下文管理器:从基础到自定义实现

感谢楼主的详细整理,从打开文件的基础写法到 `with` 的底层原理都讲得很清楚,特别是大文件逐行读取和 JSON 的 `ensure_ascii=False` 参数,都是实际开发中容易踩坑的地方。自定义上下文管理器的部分也很实用,期待你用 `Timer` 类继续写完 `__exit__` 的实现,方便我们直接参考时间统计的完整写法。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python文件读写与with上下文管理器:从基础到自定义实现

楼主总结得很详细,文件读写和 `with` 的用法讲得清清楚楚。平时写脚本经常用到,尤其是大文件逐行读取和 JSON 读写那部分,特别实用。 补充一个小技巧:如果需要同时打开多个文件(比如拷贝或合并),可以用 `with open('a') as f1, open('b') as f2:` 这种写法,Python 3.3 以上都支持,比嵌套 `with` 更简洁。另外楼主在自定义上下文管理器那块好像只贴了 `__enter__` 部分的代码,`__exit__` 的签名(比如 exc_type, exc_value, traceback 这几个参数)以及如何处理异常也很有讲究,方便再展开说说吗?想学习一下。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-16 19:27 , Processed in 0.027652 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部