在 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)
当然,如果你的withbody 真的只是一个循环files,那么没有理由这样做——只需with在循环中为每个文件添加一个语句即可。(事实上,不这样做是有充分理由的——为什么一次打开可能无限数量的文件句柄只是为了一次使用它们?)但是大概你的真实代码需要同时使用多个文件。
在早期版本中,您可以轻松地ExitStack从3.3 源代码中借用。向后移植到 3.2 很简单;对于 2.7,您需要去除(或重写,如果需要的话)那些对异常传播感兴趣的东西,以保证您正确的异常上下文,但这很容易。
然而,一个更好的解决方案可能是安装contextlib2PyPI,它“将标准库的 contextlib 模块的最新版本中的功能向后移植到早期的 Python 版本。” 然后你可以只使用contextlib2.ExitStack而不是contextlib.ExitStack. (事实上,在 Python 3.3 之前, contextlib2hadExitStack以它的初步名称......)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')执行opens 并将它们包装在 a中的 s closing_all,但我认为这不会增加太多。
当然,如果您需要经常这样做,最好的答案是opening_all围绕.ExitStackclosing_all