Python中with语句详解:作用、应用场景及其重要性
📌 Python 中的 with
语句:作用 & 什么时候用
1️⃣ with
是干嘛的?
with
主要用来 自动管理资源,确保资源(文件、数据库连接等)在使用完后能自动释放,避免资源泄露问题。
换句话说:
with
:你要自己手动释放资源,容易忘记,出 bug。with
:Python 会自动帮你释放资源,不用操心。2️⃣ 什么时候需要 with
?
凡是涉及:
✅ 打开文件
✅ 数据库连接
✅ 网络请求
✅ 多线程锁
✅ 临时性资源管理
这些场景都应该用 with
!
3️⃣ 举例说明
(1)文件操作
❌ 不使用 with
,容易忘记关闭文件:
file = open("data.txt", "r")
data = file.read()
file.close() # 🔴 你必须手动关闭文件
如果 忘了 file.close()
,可能会导致 文件一直占用,影响其他程序访问。
✅ 使用 with
,自动关闭文件
with open("data.txt", "r") as file:
data = file.read() # ✅ 读完后,Python 自动关闭文件
with
结束后,文件 file
会自动关闭!
(2)数据库操作
❌ 不使用 with
,可能忘记关闭数据库连接
import sqlite3
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
data = cursor.fetchall()
conn.close() # 🔴 必须手动关闭连接
如果程序出错,中途 return
了,conn.close()
可能不会执行,导致数据库连接泄露!
✅ 使用 with
,自动管理数据库连接
import sqlite3
with sqlite3.connect("example.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
data = cursor.fetchall() # ✅ 退出 `with` 代码块时,数据库连接自动关闭!
好处:
close()
,更安全with
也会自动关闭数据库(3)网络请求
❌ 不使用 with
,请求未关闭
import requests
response = requests.get("https://www.example.com")
data = response.text
response.close() # 🔴 必须手动关闭连接
如果忘了 response.close()
,可能会占用网络资源,影响其他请求。
✅ 使用 with
,自动关闭请求
import requests
with requests.get("https://www.example.com") as response:
data = response.text # ✅ 退出 `with`,自动关闭请求
(4)多线程锁
在多线程编程中,我们需要使用 Lock
来保证数据安全。
但如果忘记释放 lock
,程序可能会死锁!
✅ 使用 with
,确保锁释放
import threading
lock = threading.Lock()
with lock: # ✅ 进入 `with`,加锁
print("线程安全的代码")
# ✅ 退出 `with`,自动释放锁
4️⃣ with
语法是如何工作的?
with
其实是调用对象的 __enter__()
和 __exit__()
方法,确保资源正确释放:
class MyResource:
def __enter__(self):
print("资源已获取")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("资源已释放")
with MyResource():
print("使用资源中...")
输出:
资源已获取
使用资源中...
资源已释放
结论: 只要 with
结束,Python 自动调用 __exit__()
释放资源,不用手动操作。
5️⃣ 结论
✅ with
主要用于 自动管理资源,避免手动释放资源的麻烦。
✅ 避免资源泄露,减少 bug,即使代码中途出错,也能自动清理资源。
✅ 常用于:
💡 记住:凡是涉及“打开 -> 使用 -> 关闭”的场景,都应该用 with
! 🚀
📌 with
结束的时机是什么时候?
with
语句的结束时机,简单来说,就是**with
代码块执行完毕**,或者在 with
代码块中发生异常时。
具体来说,with
语句在退出代码块时,会触发资源的自动清理,也就是调用 __exit__()
方法。
1️⃣ 具体的结束时机
✅ 情况 1:正常执行完代码块
如果 with
代码块里的代码正常执行完,就会自动调用 __exit__()
方法释放资源,with
语句结束。
🔹 示例:文件操作
with open("test.txt", "w") as f:
f.write("Hello, world!") # ✅ 正常执行完 `write`
# `with` 结束,文件自动关闭
结束时机: f.write()
执行完,with
退出,文件自动关闭。
✅ 情况 2:发生异常,with
也会立即结束
如果 with
代码块里发生异常,with
语句会立刻调用 __exit__()
进行清理,然后抛出异常(除非 __exit__()
处理了异常)。
🔹 示例:发生异常
try:
with open("test.txt", "w") as f:
f.write("Hello, world!")
1 / 0 # ❌ 人为制造异常
except ZeroDivisionError:
print("发生错误,文件已经关闭")
结束时机: 1 / 0
抛出异常时,with
立刻结束,文件自动关闭。
✅ 情况 3:return
也会触发 with
结束
如果 with
代码块里遇到 return
,break
,continue
,with
也会立即结束,并释放资源。
🔹 示例:return
触发 with
结束
def write_file():
with open("test.txt", "w") as f:
f.write("Hello, world!")
return # ✅ `return` 触发 `with` 结束
结束时机: return
触发 with
结束,文件自动关闭。
2️⃣ with
结束的底层原理
当 with
语句执行时,它的工作流程如下:
- 进入
with
:调用__enter__()
方法,获取资源。 - 执行代码块:执行
with
代码块里的内容。 - 退出
with
: - 正常结束:执行
__exit__()
,释放资源。 - 发生异常:
- 如果
__exit__()
处理异常,则with
正常结束。 - 如果
__exit__()
没有处理异常,异常会继续向上抛出。
🔹 示例:自定义 with
语句行为
class MyContext:
def __enter__(self):
print("进入 `with` 语句")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出 `with` 语句")
if exc_type:
print(f"发生异常:{exc_val}")
return True # 处理异常,不抛出
with MyContext():
print("执行 `with` 代码块")
1 / 0 # ❌ 发生异常
print("代码继续执行")
输出:
进入 `with` 语句
执行 `with` 代码块
退出 `with` 语句
发生异常:division by zero
代码继续执行
分析:
1 / 0
触发异常,with
立刻结束,调用 __exit__()
处理。__exit__()
返回 True
,异常被处理,不会向上传播。3️⃣ 总结
✅ with
语句的结束时机:
- 正常执行完代码块(执行完最后一行)。
- 遇到异常,
with
立刻结束,并调用__exit__()
释放资源。 - 遇到
return
、break
、continue
,with
立刻结束,资源被释放。
💡 简单记住:with
一旦代码块结束或发生异常,就立刻释放资源,自动退出! 🚀
作者:背太阳的牧羊人