Python中的"with"语句有什么用?
我正在尝试理解Python中的with语句。无论我在哪里查资料,大家都在讲它是用来打开和关闭文件的,而且它的作用是替代try-finally这个结构。能不能有人提供一些其他的例子呢?我最近在试用Flask,里面到处都是with语句。希望有人能帮我更清楚地理解这个概念。
3 个回答
在 PEP343 中,有十二个使用 with
的例子,其中包括打开文件的例子:
- 一个模板,用来确保在进入一个代码块时获取的锁,在离开这个代码块时会被释放。
- 一个打开文件的模板,确保在离开这个代码块时文件会被关闭。
- 一个用于提交或回滚数据库事务的模板。
- 第一个例子的重写版本,没有使用生成器。
- 暂时重定向标准输出。
- 一个变种的 opened(),它还会返回错误状态。
- 另一个有用的例子是一个阻止信号的操作。
- 这个功能的另一个用途是处理 Decimal(十进制)上下文。
- 这是一个简单的十进制模块的上下文管理器。
- 一个通用的“对象关闭”上下文管理器。
- 一个 released() 上下文,用于暂时释放之前获取的锁,通过交换 acquire() 和 release() 的调用。
- 一个“嵌套”的上下文管理器,自动从左到右嵌套提供的上下文,以避免过多的缩进。
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:
时,就不需要这样的检查——我可以立刻看到锁会被正确释放。
这里有一个很好的解释 在这里。简单来说,with语句会在相关的对象上调用两个特殊的方法,分别是 __enter__ 和 __exit__ 方法。__enter__ 方法会返回和 "with" 语句相关联的变量。而 __exit__ 方法则是在语句执行完后被调用,用来处理一些清理工作,比如关闭文件指针。