3

如果我想使用两个文件,我可以编写:

with open(fname1, 'r') as f1, open(fname2, 'r') as f2:
    # do stuff with f1 and f2

但是,如果我有一个路径列表(例如,来自 glob.glob)怎么办?我可以在列表理解中做类似的事情吗?我想到了类似的东西:

with [open(path, 'r') for path in paths_list] as flist:
    # do stuff with this list of open file objects

正如所写,这是行不通的。

4

3 回答 3

8

语句的对象with必须是上下文管理器。所以,不,你不能用一个列表来做到这一点,但你可以用一个自定义容器来做到这一点。

请参阅:http ://docs.python.org/2/library/contextlib.html

或者,对于 3.3+,有这个:http ://docs.python.org/dev/library/contextlib.html#contextlib.ExitStack (注意,根据 arbarnert 的回答,这可以在 2.7 中使用,使用 contextlib2。见他的回答链接。)

这里的实际解决方案可能是如果您不打算使用contextlib2将上下文管理器置于循环中:

for path in paths_list:
    with open(path, 'r') as f:
         #whatever
         pass

编辑:显然,上面将一次打开您的文件。您需要同时打开不确定数量的文件的用例相对较少。

编辑:一次打开多个文件,ExitStack是您正在寻找的解决方案。

于 2013-07-16T19:16:25.613 回答
6

在 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在循环中为每个文件添加一个语句即可。(事实上​​,这样做是有充分理由的——为什么一次打开可能无限数量的文件句柄只是为了一次使用它们?)但是大概你的真实代码需要同时使用多个文件。


在早期版本中,您可以轻松地ExitStack3.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

于 2013-07-16T20:17:58.660 回答
2

您可以使用fileinput

import fileinput

for line in fileinput.input(fileList):
    ...
于 2013-07-16T19:18:20.973 回答