【问题标题】:Open a list of files using with/as context manager使用 with/as 上下文管理器打开文件列表
【发布时间】:2013-10-25 02:18:45
【问题描述】:

注意:我知道

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

【问题讨论】:

标签: python with-statement contextmanager


【解决方案1】:

这个怎么样?

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 context manager API

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

import contextlib

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

【讨论】:

  • contextlib.nested 已弃用
  • 谢谢,已更新。我在这里给出的ListContext 有相同的警告。即如果内部元素的__enter__()__exit__() 触发异常,就会出现问题。
【解决方案2】:

听起来您基本上是在寻找 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]

【讨论】:

  • 无法将with 语句的多管理器形式与任意长度的上下文管理器列表一起使用。
【解决方案3】:

如果您可以访问 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-30
    • 1970-01-01
    • 2021-06-03
    • 2019-05-30
    相关资源
    最近更新 更多