在 3.3+ 中,ExitStack
绝对是答案;事实上,这是文档中给出的第一个示例:
with ExitStack() as stack:
files = [stack.enter_context(open(path) for path in path_list]
for f in files:
do_something(f)
当然,如果你的with
body 真的只是一个循环files
,那么没有理由这样做——只需with
在循环中为每个文件添加一个语句即可。(事实上,不这样做是有充分理由的——为什么一次打开可能无限数量的文件句柄只是为了一次使用它们?)但是大概你的真实代码需要同时使用多个文件。
在早期版本中,您可以轻松地ExitStack
从3.3 源代码中借用。向后移植到 3.2 很简单;对于 2.7,您需要去除(或重写,如果需要的话)那些对异常传播感兴趣的东西,以保证您正确的异常上下文,但这很容易。
然而,一个更好的解决方案可能是安装contextlib2
PyPI,它“将标准库的 contextlib 模块的最新版本中的功能向后移植到早期的 Python 版本。” 然后你可以只使用contextlib2.ExitStack
而不是contextlib.ExitStack
. (事实上,在 Python 3.3 之前, contextlib2
hadExitStack
以它的初步名称......)ContextStack
但是您也可以轻松地构建一个closing_all
上下文管理器,类似于 stdlib 的closing
但有多种用途:
@contextlib.contextmanager
def closing_all(things):
try:
yield things
finally:
for thing in things:
thing.close()
如果您需要处理close
方法可以引发的事情,您需要更聪明一点——但是对于文件对象以及您使用的大多数其他类型closing
,您不需要这样做。
更大的问题是,如果任何open
可以引发异常,则很难找到任何可以实际作为things
参数传递的有效序列。但如果这不是问题,使用它甚至比ExitStack
:
with closing_all(open(path) for path in path_list) as files:
for f in fs:
do_something(f)
您还可以构建一个为您opening_all(paths, mode='r')
执行open
s 并将它们包装在 a中的 s closing_all
,但我认为这不会增加太多。
当然,如果您需要经常这样做,最好的答案是opening_all
围绕.ExitStack
closing_all