21

注意:我知道

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:]

4

3 回答 3

25

如果您可以访问 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
于 2013-10-16T20:02:37.053 回答
6

这个怎么样?

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>]

请注意,它们在带有上下文的内部是打开的,而在外部是封闭的。

这是使用 Python上下文管理器 API

编辑:这似乎已经存在但已被弃用:参见contextlib这个 SO question。像这样使用它:

import contextlib

with contextlib.nested(*[open(fn, 'w') for fn in arr]) as files:
    print files
print files
于 2013-10-16T19:53:01.420 回答
0

听起来您基本上是在寻找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]
于 2013-10-16T20:02:09.423 回答