我试图理解 python 中的 with 语句。我到处都在谈论打开和关闭文件,并且旨在替换 try-finally 块。有人也可以发布一些其他示例。我只是在尝试烧瓶,里面有很多陈述。绝对要求有人提供一些澄清。
3 回答
该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__ 方法在语句执行后调用以处理任何清理(例如关闭文件指针)。
with
在PEP343中有 12 个使用示例,包括文件打开示例:
- 用于确保在块开始时获取的锁在离开块时释放的模板
- 用于打开文件的模板,可确保在离开块时关闭文件
- 用于提交或回滚数据库事务的模板
- 示例 1 在没有生成器的情况下重写
- 临时重定向标准输出
- opens() 的变体,它也返回错误条件
- 另一个有用的例子是阻塞信号的操作
- 此功能的另一个用途是十进制上下文
- 这是十进制模块的简单上下文管理器
- 一个通用的“对象关闭”上下文管理器
- 一个released() 上下文,通过交换acquire() 和release() 调用来临时释放先前获得的锁
- 一个“嵌套”上下文管理器,它自动从左到右嵌套提供的上下文以避免过度缩进