Python 的 with语句介绍
with 语句用于通过一个上下文管理器(Context Manager)包裹(wrap)一段代码块的执行。上下文管理器定义了一些方法,可以在代码块执行前后自动处理特定的操作,比如资源的分配和释放。这种做法可以帮助我们简化常见的 try...except...finally 模式,使其易于重用。
【with 语句 官方介绍
https://docs.python.org/zh-cn/3/reference/compound_stmts.html#with 】
比较难懂?换一种说法,Python 的 with语句,主要用于简化资源管理和异常处理。它通常用于打开文件、控制数据库连接或操作其他需要在使用后,自动关闭或释放资源的情况——在完成对某个资源的操作后,能自动进行清理工作,比如关闭文件或自动关闭数据库连接等。
“上下文管理器”是一个实现了特定方法(如__enter__和__exit__)的对象。with 语句背后的魔法是通过两个特殊方法实现的:
__enter__:开始时调用,相当于“准备使用”"
__exit__:结束时调用,相当于“用完了,收拾一下”
with 语句就是一个很贴心的助手,它帮你处理了那些你可能会忘记,但又很重要的细节。你甚至可以为自己的对象添加 with 支持。这就像是你制定了一个“使用规则”:怎么开始使用,怎么结束使用。
下面通俗地理解一下 with 语句
想象一下,你在厨房里做饭:
你打开水龙头(开始使用资源)。
洗完菜后,记得关掉水龙头(清理资源)。
如果你忘了关水龙头,就可能造成浪费。同样,在编程中,如果不及时关闭文件或连接,可能会导致内存泄漏或其他问题。
with语句是一种让你的代码更简洁、更安全的方法,可以确保正确地管理资源,而不必担心手动处理每一步。这就像是在厨房里,有人帮你负责关水龙头一样,让你专注于做饭。
简单地说,with语句的工作原理:它使用“上下文管理器”来控制代码块的执行前后应该做什么。作用:它简化了资源管理和错误处理,使代码更加整洁和安全。使用 with 语句后,常见的资源管理模式(比如打开文件、进行数据库连接等)可以通过简单的语法来实现,而无需手动管理所有可能的异常情况和资源释放工作。
with语句的基本语法:
with expression as variable:
# 代码块
其中,expression:通常是一个可以上下文管理的对象,比如文件、数据库连接等。
variable:用于引用上下文管理器返回的对象(如文件对象)。
例如
with open('file.txt', 'r') as file:
content = file.read()
print(content)
这里发生了什么?
通过 open('file.txt', 'r') 打开一个文件。
as file 将打开的文件对象赋值给变量 file
在 with 块中,你可以安全地读取这个文件,如print(content)。
当程序离开这个块时,Python 会自动关闭这个文件,不管是否发生异常。这样,你就不用担心忘记手动关闭文件的问题了!
这比手动打开和关闭文件更加安全和简洁。
上面代码,若不使用 with 语句,打开文件并确保其在使用后正确关闭的代码可以写成下面这样:
file = open('file.txt', 'r') # 打开文件
try:
content = file.read() # 读取文件内容
print(content) # 打印内容
finally:
file.close() # 确保关闭文件
在这个示例中,使用了 try...finally 语句来确保即使在读取文件内容或打印时发生异常,文件也能够被正确关闭。这是手动管理文件资源的一种常见方式,但相较于 with 语句,代码较为繁琐且容易出错。因此,推荐在可能的情况下使用 with 语句。
以下是使用和不使用 with 语句处理数据库(以 SQLite 为例)的示例代码。
顺便提示,Python 自带了 SQLite 数据库的支持,SQLite 就被集成到 Python 标准库中,通过 sqlite3 模块提供。SQLite 是一个轻量级的数据库,广泛用于小型应用和嵌入式系统。不需要单独的服务器进程或配置,数据库文件是一个普通文件,可以很方便地进行复制和备份。数据库文件可以在不同操作系统之间*移动。
使用 with 语句情况:
import sqlite3
# 使用 with 语句管理数据库连接和游标
with sqlite3.connect('example.db') as conn: # 连接到数据库
with conn.cursor() as cursor: # 创建游标
cursor.execute('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)')
cursor.execute('INSERT INTO users (name) VALUES (?)', ('Alice',))
conn.commit() # 提交事务
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
for row in rows:
print(row) # 打印结果
不使用 with 语句情况:
import sqlite3
# 手动管理数据库连接和游标
conn = sqlite3.connect('example.db') # 连接到数据库
cursor = conn.cursor() # 创建游标
try:
cursor.execute('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)')
cursor.execute('INSERT INTO users (name) VALUES (?)', ('Alice',))
conn.commit() # 提交事务
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
for row in rows:
print(row) # 打印结果
finally:
cursor.close() # 关闭游标
conn.close() # 关闭连接
解释说明
在使用 with 语句时,Python 会自动管理资源,在代码块结束后自动关闭连接和游标,这使得代码更简洁且不容易出错。
在没有使用 with 语句的情况下,需要手动调用 close() 方法来释放资源,这增加了出错的可能性,特别是在发生异常时。
我们可以为自己的对象添加 with 支持,方法是实现上下文管理器协议。具体来说,你需要定义 __enter__ 和 __exit__ 方法。
__enter__ 方法在进入 with 块时被调用,可以用于设置资源或状态并返回一个对象。
__exit__ 方法在退出 with 块时被调用,用于清理或释放资源。
以下是一个简单易懂的例子:创建一个自定义上下文管理器。
# 创建一个上下文管理器类
class SimpleFile:
def __init__(self, filename):
self.filename = filename
self.file = None
def __enter__(self):
self.file = open(self.filename, 'w') # 打开文件
return self.file # 返回文件对象
def __exit__(self, exc_type, exc_value, traceback):
if self.file:
self.file.close() # 关闭文件
# 如果发生异常,可以选择返回 True 以抑制异常
# return True
# 使用上下文管理器
with SimpleFile('example.txt') as f:
f.write('Hello, world!\n') # 向文件写入内容
解释说明
初始化: __init__ 方法接收文件名并初始化 file 属性。
进入上下文: 在 __enter__ 方法中,打开文件并返回文件对象,供 with 块内部使用。
退出上下文: 在 __exit__ 方法中,关闭文件。如果在 with 块中出现异常,exc_type、exc_value 和 traceback 会包含相关信息(可以根据需要处理)。
在这个示例中,SimpleFile 类打开了一个文件以进行写操作,文件会在 with 块结束后自动关闭。