文件读写是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__()。
执行流程:
等价于:- 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及其上下文管理机制,可以在日志处理、数据落地、接口调试等场景中直接应用,提升代码健壮性。 |