是的,Python 之禅说“扁平胜于嵌套”,但这并不是我们关心的唯一特性;它还指出“简单胜于复杂”。它的美妙之with
处在于它实际上遵守了这两个原则,我将在下面解释。
每当您发现自己对 Python 中的某个功能进行哲学思考时,可能都值得查找Python 增强提案 (PEP)以了解该功能背后的动机。在这种情况下,PEP 343——“with”语句在摘要中预先说明:
此 PEP 向 Python 语言添加了一个新语句“with”,以便可以排除 try/finally 语句的标准用法。
分解try/finally
语句使代码更简单,更具可读性。
然而, PEP 343比提供一些简单的语法糖更深入。它建立了一个上下文管理器协议:
语句中紧跟 with 关键字的表达式是“上下文表达式”,因为该表达式提供了上下文管理器在语句主体期间建立的运行时环境的主要线索。
使用上下文管理器协议,API 编写者可以帮助隐藏复杂性并确保在多线程上下文中正确获取/释放资源。
但是PEP 343with
的示例 12 显示了该语句的真正优点,它解释了:
一个“嵌套”上下文管理器,它自动从左到右嵌套提供的上下文以避免过度缩进。
使用nested()
上下文管理器,您可以获取如下所示的代码:
with a as x:
with b as y:
with c as z:
# Perform operation
并将其变成这样:
with nested(a, b, c) as (x, y, z):
# Perform operation
请注意,它nested()
是在 Python 2.5 中引入的,但从 2.7 版开始,它已被弃用,而支持这种多上下文管理器语法形式:
with a as x, b as y, c as z:
# Perform operation
显然,这不仅更简单、更易读,而且比嵌套更扁平。因此,使用with
是遵循无为的路径:)
更新:为了回应对 Simeon Visser 的回答的评论,这里是一个示例,当您想将两个(或多个)文件的内容压缩在一起时,您可能会使用多个上下文管理器一次打开多个文件,这样如果打开其中一个文件失败,它将使整个事情失败并正确关闭每个打开的文件:
from itertools import izip
with open("/etc/passwd") as a, open("/etc/group") as b, open("/etc/shadow") as c:
for lines in izip(a,b,c):
print map(lambda x: x.split(':')[0], lines)
运行此示例两次;一次以root身份,一次以普通用户身份。假设您将此文件保存为ziptogether.py
第一次尝试以 root 身份调用它sudo python ziptogether.py
并且它会成功,但是作为普通用户调用它python ziptogether.py
会失败,因为您没有读取权限/etc/shadow
。当它失败时,上下文管理器将确保在执行移出with
语句范围时正确关闭失败之前成功打开的文件。