Python中的"with"语句有什么用?

3 投票
3 回答
1190 浏览
提问于 2025-04-16 04:42

我正在尝试理解Python中的with语句。无论我在哪里查资料,大家都在讲它是用来打开和关闭文件的,而且它的作用是替代try-finally这个结构。能不能有人提供一些其他的例子呢?我最近在试用Flask,里面到处都是with语句。希望有人能帮我更清楚地理解这个概念。

3 个回答

1

PEP343 中,有十二个使用 with 的例子,其中包括打开文件的例子:

  1. 一个模板,用来确保在进入一个代码块时获取的锁,在离开这个代码块时会被释放。
  2. 一个打开文件的模板,确保在离开这个代码块时文件会被关闭。
  3. 一个用于提交或回滚数据库事务的模板。
  4. 第一个例子的重写版本,没有使用生成器。
  5. 暂时重定向标准输出。
  6. 一个变种的 opened(),它还会返回错误状态。
  7. 另一个有用的例子是一个阻止信号的操作。
  8. 这个功能的另一个用途是处理 Decimal(十进制)上下文。
  9. 这是一个简单的十进制模块的上下文管理器。
  10. 一个通用的“对象关闭”上下文管理器。
  11. 一个 released() 上下文,用于暂时释放之前获取的锁,通过交换 acquire() 和 release() 的调用。
  12. 一个“嵌套”的上下文管理器,自动从左到右嵌套提供的上下文,以避免过多的缩进。
10

with语句的主要目的是让“做正确的事情”变得简单易行。虽然文件操作的例子是最简单的,但线程锁其实是一个更经典的例子,能说明代码中不明显的错误:

try:
    lock.acquire()
    # do stuff
finally:
    lock.release()

这段代码是有问题的——如果获取锁失败,要么会抛出错误(因为代码会尝试释放一个它根本没有获取的锁),要么更糟糕的是,如果这是一个递归锁,它会提前释放。正确的代码应该是这样的:

lock.acquire()
try:
    # do stuff
finally:
    # If lock.acquire() fails, this *doesn't* run
    lock.release()

通过使用with语句,就不可能出错,因为它已经内置在上下文管理器中了:

with lock: # The lock *knows* how to correctly handle acquisition and release
  # do stuff

with语句的另一个好处类似于函数和类装饰器的主要优点:它将“分开的两部分”代码(比如装饰器的函数定义,或者当前例子中的try块)变成“一整块”代码,程序员只需提前声明他们想要做的事情。

对于短小的例子,这看起来似乎没有太大好处,但在审查代码时,这实际上会带来巨大的不同。当我在代码中看到lock.acquire()时,我需要向下滚动去检查是否有对应的lock.release()。而当我看到with lock:时,就不需要这样的检查——我可以立刻看到锁会被正确释放。

9

这里有一个很好的解释 在这里。简单来说,with语句会在相关的对象上调用两个特殊的方法,分别是 __enter__ 和 __exit__ 方法。__enter__ 方法会返回和 "with" 语句相关联的变量。而 __exit__ 方法则是在语句执行完后被调用,用来处理一些清理工作,比如关闭文件指针。

撰写回答