Python文件读写与with上下文管理器:从基础到自定义实现
文件读写是Python日常开发中最常见的I/O操作之一,包括日志处理、配置文件解析、接口数据落地等场景。Python内置的open()函数配合with语句,能安全高效地管理文件资源,避免资源泄露。本文将从基础用法到with的底层机制,结合代码示例进行完整梳理。基础操作:open / read / write
Python通过open()打开文件,返回文件对象。基本参数包括模式('r'只读,'w'写入覆盖,'a'追加,'rb'/'wb'二进制)和编码(推荐encoding='utf-8')。使用后必须调用close()释放资源。
f = open("test.txt", "r", encoding="utf-8")
content = f.read()
print(content)
f.close()
写文件示例:
f = open("output.txt", "w", encoding="utf-8")
f.write("Hello, Python I/O")
f.close()
注意:忘记close()会导致资源未释放;'w'模式会清空原有文件内容。
文本文件处理与逐行读取
处理文本时更常用逐行循环:
f = open("test.txt", "r", encoding="utf-8")
for line in f:
print(line.strip())
f.close()
逐行读取比read()一次性加载所有内容更适合大文件。
使用with语句(上下文管理器)简化文件操作
with语句自动管理文件资源,代码块结束后自动关闭文件,即使中间发生异常也能保证释放:
with open("test.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)
这等价于:打开文件 -> 使用文件 -> 自动调用close()。推荐作为标准写法。
处理大文件(逐行读取)
几百MB的文件不能一次性read()进内存,推荐逐行读取:
with open("big_file.txt", "r", encoding="utf-8") as f:
for line in f:
process(line)
更细粒度的控制:
with open("big_file.txt", "r", encoding="utf-8") as f:
while True:
line = f.readline()
if not line:
break
process(line)
这种方式只保留当前行在内存中,适用于日志分析、数据清洗等场景。
JSON文件读写(高频场景)
接口返回值、配置文件、爬虫结果常用JSON格式。Python内置json模块直接读写:
读取JSON:
import json
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
print(data)
写入JSON:
import json
data = {"name": "Alice", "age": 18}
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
参数说明:ensure_ascii=False避免中文被转义;indent=2美化输出。
with的本质:上下文管理器
with不仅用于文件,其核心是上下文管理机制(Context Management)。依赖的对象需实现两个魔术方法:__enter__()和__exit__()。
执行流程:
with obj as x:
# 代码块
等价于:
x = obj.__enter__()
try:
# 代码块
finally:
obj.__exit__()
__enter__()进入时做资源初始化,__exit__()退出时做资源释放。
with的其他常见用法
1. 线程锁
import threading
lock = threading.Lock()
with lock:
# 临界区代码
print("线程安全操作")
作用:自动加锁(__enter__)和自动释放锁(__exit__),避免忘记release()导致死锁。
2. 数据库连接/事务管理
很多数据库库支持with自动提交或回滚事务:
with connection:
cursor.execute("INSERT INTO table VALUES (1)")
或自动关闭游标:
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM table")
3. 临时修改环境(上下文切换)
from decimal import localcontext, Decimal
with localcontext() as ctx:
ctx.prec = 2
print(Decimal("1.12345") + Decimal("2.34567"))
print(Decimal("1.12345") + Decimal("2.34567"))
在with内修改配置,退出后自动恢复原状态。
自定义上下文管理器
可以自己定义类实现__enter__和__exit__:
class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
print("耗时:", time.time() - self.start)
with Timer():
sum(range(1000000))
可用于统计耗时、日志记录、资源管理等。
更优雅的写法:contextlib
Python的contextlib模块提供@contextmanager装饰器,减少代码量:
from contextlib import contextmanager
import time
@contextmanager
def timer():
start = time.time()
yield
print("耗时:", time.time() - start)
with timer():
sum(range(1000000))
相比写类更直观简洁。
总结
with的核心是让“必须成对出现的操作”(获取/释放、开始/结束)变得安全且简洁。常见组合:
- 打开/关闭(文件)
- 加锁/解锁(线程)
- 开始/提交/回滚(数据库)
- 设置/恢复(环境)
熟练掌握with及其上下文管理机制,可以在日志处理、数据落地、接口调试等场景中直接应用,提升代码健壮性。
Re: Python文件读写与with上下文管理器:从基础到自定义实现
感谢楼主的详细整理,从打开文件的基础写法到 `with` 的底层原理都讲得很清楚,特别是大文件逐行读取和 JSON 的 `ensure_ascii=False` 参数,都是实际开发中容易踩坑的地方。自定义上下文管理器的部分也很实用,期待你用 `Timer` 类继续写完 `__exit__` 的实现,方便我们直接参考时间统计的完整写法。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 这几个参数)以及如何处理异常也很有讲究,方便再展开说说吗?想学习一下。Re: Python文件读写与with上下文管理器:从基础到自定义实现
非常感谢楼主这么系统地梳理了文件读写和上下文管理器的用法,从基础 open/read/write 到 with 语句的原理,再到自定义实现,内容既完整又实用。特别是对大文件逐行处理、JSON 读写以及 with 在锁、数据库连接等场景的拓展,很有参考价值。我以前只知道 with 能自动关闭文件,但没深入想过 __enter__ 和 __exit__ 的机制,看了楼主的解释清晰多了。建议楼主在自定义上下文管理器部分补充一个示例的 __exit__ 处理异常的写法,比如返回 True 来抑制异常的场景,这样对初学者会更完整。总之,这篇总结很适合作为参考笔记,感谢分享!
页:
[1]