使用/作为上下文管理器打开文件列表

时间:2013-10-16 19:44:52

标签: python with-statement contextmanager

注意:我知道

with open('f1') as f1, open('f2') as f2:
    ...

语法。这是一个不同的问题。


给定字符串列表file_names有一种方法可以使用with / as使用单行打开每个文件名。例如:

with [open(fn) for fn in file_names] as files:
    # use the list of files

当然,它尝试在列表中使用上下文管理器时不起作用。在运行时之前可能无法知道列表的长度,例如sys.argv[1:]

3 个答案:

答案 0 :(得分:15)

如果您可以访问Python 3.3+,则会有一个专门为此目的设计的特殊类:ExitStack。它的工作方式与您期望的一样:

with contextlib.ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # All opened files will automatically be closed at the end of
    # the with statement, even if attempts to open files later
    # in the list raise an exception

答案 1 :(得分:7)

这个怎么样?

class ListContext:
    def __init__(self, l):
        self.l = l

    def __enter__(self):
        for x in self.l:
            x.__enter__()
        return self.l

    def __exit__(self, type, value, traceback):
        for x in self.l:
            x.__exit__(type, value, traceback)

arr = ['a', 'b', 'c']

with ListContext([open(fn, 'w') for fn in arr]) as files:
    print files

print files

输出是:

[<open file 'a', mode 'w' at 0x7f43d655e390>, <open file 'b', mode 'w' at 0x7f43d655e420>, <open file 'c', mode 'w' at 0x7f43d655e4b0>]
[<closed file 'a', mode 'w' at 0x7f43d655e390>, <closed file 'b', mode 'w' at 0x7f43d655e420>, <closed file 'c', mode 'w' at 0x7f43d655e4b0>]

注意,它们在with context中打开并在外面关闭。

这是使用Python context manager API

编辑:看起来这已经存在但已弃用:请参阅contextlibthis SO question。像这样使用它:

import contextlib

with contextlib.nested(*[open(fn, 'w') for fn in arr]) as files:
    print files
print files

答案 2 :(得分:1)

听起来你基本上都在寻找contextlib.nested(),这在Python 2.7中被弃用,而不是with语句的多管理器形式,但正如文档中所述:

  

此函数相对于with语句的多管理器形式的一个优点是参数解包允许它与可变数量的上下文管理器一起使用

如果你使用的是Python 3.x,这里是Python 2.7源码的代码:

from contextlib import contextmanager

@contextmanager
def nested(*managers):
    """Combine multiple context managers into a single nested context manager.                                                                                                               
   This function has been deprecated in favour of the multiple manager form
   of the with statement.

   The one advantage of this function over the multiple manager form of the
   with statement is that argument unpacking allows it to be
   used with a variable number of context managers as follows:

      with nested(*managers):
          do_something()

    """
    warn("With-statements now directly support multiple context managers",
         DeprecationWarning, 3)                                                                                                                                                                  exits = []
    vars = []
    exc = (None, None, None)
    try:
        for mgr in managers:
            exit = mgr.__exit__
            enter = mgr.__enter__
            vars.append(enter())
            exits.append(exit)
        yield vars
    except:
        exc = sys.exc_info()
    finally:
        while exits:
            exit = exits.pop()
            try:
                if exit(*exc):
                    exc = (None, None, None)
            except:
                exc = sys.exc_info()
        if exc != (None, None, None):
            # Don't rely on sys.exc_info() still containing
            # the right information. Another exception may
            # have been raised and caught by an exit method
            raise exc[0], exc[1], exc[2]