你有没有遇到过这种崩溃的情况:
f = open('data.txt', 'w')f.write('重要数据')# 程序在这里崩了,文件没关闭!然后你发现文件损坏了,数据丢了,客户在骂娘,老板在瞪你...
更绝的是这种情况:
conn = sqlite3.connect('database.db')cursor = conn.cursor()cursor.execute('UPDATE users SET money = money + 1000 WHERE id = 1') #后端 #每天一个知识点# 这里抛异常了,连接没关闭,数据库锁死了!整个应用卡住,用户投诉电话被打爆,你在凌晨3点重启服务器...
今天咱们就来彻底搞懂 Python 的 with 语句,让你告别这些资源泄漏的噩梦。
什么是with语句?
简单来说,with 语句就是 Python 的"自动保姆"。
它会在你使用资源的时候自动打开,用完了自动关闭,中间出异常了也自动关闭。
就像雇了个保姆,你出门前告诉他"帮我照顾孩子",你回家时孩子已经被哄睡着了,不管中间孩子怎么闹腾,保姆都搞定了。
没用with的惨痛教训 ❌ 新手写法(我当年也这么写过)
def process_data(): file = open('important.txt', 'w') File.write('关键数据') # 忘记关闭文件了!# 文件句柄泄漏,系统资源被占用结果: 文件可能没保存成功,其他程序无法访问这个文件。
❌ 进阶写法(看起来很安全,实则坑爹)
def process_data(): try: file = open('important.txt', 'w') file.write('关键数据') # 这里如果抛异常,file.close()不会执行!1 / 0 # 假设这里出异常了file.close() except Exception as e: print(f'出错了: {e}') # 文件没关闭!资源泄漏!结果: 异常处理了,但资源没释放。
C ❌ 老手写法(终于知道要close了)
def process_data(): try: file = open('important.txt', 'w') file.write('关键数据') except Exception as e: print(f'出错了: {e}') finally: file.close() # 但是如果open就失败了怎么办?结果: 如果 open('important.txt') 本身就失败,file.close() 会报错 NameError !
with语句的正确姿势 ✅ 大神写法(优雅、安全、简洁)
def process_data(): with open('important.txt', 'w') as file: file.write('关键数据') 1 / 0 # 就算这里崩了,文件也会自动关闭就这么简单!不管中间发生什么,文件都会被正确关闭。
这就是为什么 Python 大神都在用 with 的原因——它能让你睡个安稳觉。
with的工作原理
with 语句背后是两个魔术方法:__enter__ 和 __exit__ 。
class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = Nonedef __enter__(self): print('准备打开文件...') self.file = open(self.filename, self.mode) return self.file # 这个返回值会赋给 as 后面的变量def __exit__(self, exc_type, exc_val, exc_tb): print('准备关闭文件...') if self.file: self.file.close()# 如果返回True,异常会被忽略# 如果返回False或None,异常会继续传播return False# 使用方式with FileManager('test.txt', 'w') as f: f.write('Hello World') # 这里就算抛异常,__exit__也会被调用你可以把 with 想象成一个超级靠谱的管家:
- 你要进门前,管家提前帮你开门( __enter__ )
- 你在屋里随便折腾,管家在门口等着
- 你要出门时,管家帮你关门( __exit__ )
- 就算你在屋里摔倒了,管家也会先扶你起来,再帮你关门
# 读取大文件,不用担心忘记关闭with open('big_data.csv', 'r') as f: for line in f: process_line(line) # 就算这里内存溢出,文件也会关闭2. 数据库连接import sqlite3def update_user_money(user_id, amount): with sqlite3.connect('app.db') as conn: cursor = conn.cursor() cursor.execute('UPDATE users SET money = money + ? WHERE id = ?', (amount, user_id)) # 自动提交和关闭,就算这里抛异常也不会锁死数据库3. 线程锁import threadinglock = threading.Lock()def critical_section(): with lock: # 临界区代码,同时只能一个线程访问shared_resource += 1 # 就算这里抛异常,锁也会被释放,不会死锁4. 网络请求import requestsdef download_data(): with requests.Session() as session: session.auth = ('user', 'pass') response = session.get('https://api.example.com/data') # 连接池自动管理,不用担心资源泄漏自定义上下文管理器有时候你想创建自己的"自动管家",很简单:
from contextlib import contextmanager@contextmanager def timer(name): import time start = time.time() print(f'{name} 开始执行...') try: yield # 这里是 with 块中的代码finally: end = time.time() print(f'{name} 执行完毕,耗时:{end - start:.2f}秒')# 使用方式with timer('数据处理'): time.sleep(2) # 模拟耗时操作# 这里会自动计算并打印执行时间踩坑指南 1. __exit__的返回值陷阱class BadContext: def __enter__(self): print('进入') return selfdef __exit__(self, exc_type, exc_val, exc_tb): print('退出') return True # 这行是陷阱!with BadContext(): 1 / 0 # 这个异常会被"吃掉"!print('程序继续运行,你都不知道出过异常!')切记: 除非你真的知道自己在做什么,否则 __exit__ 应该返回 False 或 None 。
2. 异常信息丢失
def debug_context(): try: with some_context(): risky_operation() except Exception as e: # 这里的e可能是上下文管理器抛出的异常# 而不是你真正想捕获的异常logger.error(f'出错了: {e}')3. 嵌套with的性能# ❌ 这样写性能不好with open('file1.txt') as f1: with open('file2.txt') as f2: with open('file3.txt') as f3: process_files(f1, f2, f3)# ✅ 推荐写法with open('file1.txt') as f1, \ open('file2.txt') as f2, \ open('file3.txt') as f3: process_files(f1, f2, f3)进阶技巧 C 1. 使用contextlib.suppressfrom contextlib import suppress# 有时候你就是想忽略某些异常with suppress(FileNotFoundError): os.remove('temp_file.txt') # 文件不存在也没关系C 2. 使用contextlib.nullcontextfrom contextlib import nullcontext# 有时候你条件性地需要上下文管理器context_manager = lock if use_lock else nullcontext()with context_manager: shared_operation()总结记住一句话:涉及资源的获取和释放,就考虑用 with 语句。
- 文件操作?用with
- 数据库连接?用with
- 网络请求?用with
- 线程锁?用with
- 临时目录?用with
with 不是语法糖,是你的保险单。
下次再看到 open() 后面没有 with ,你就知道该说什么了:
"兄弟,你这代码能跑,但是我建议你买个保险..."
--- 你在项目里遇到过哪些资源泄漏的坑?评论区聊聊,看看谁的故事更惨!
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.