Python 的 with语句介绍

时间:2024-10-12 21:00:05

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 块结束后自动关闭。