Question

Note: I am aware of the

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

syntax. This is a different question.


Given a list of strings file_names is there a way using with/as to open every file name in that using a single line. Something such as:

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

which of course doesn't work as it attempts to use the context manager on a list. The length of the list may not be known until run-time, such as sys.argv[1:]

Was it helpful?

Solution

If you have access to Python 3.3+, there is a special class designed exactly for this purpose: the ExitStack. It works just like you'd expect:

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

OTHER TIPS

How about this?

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

Output is:

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

Notice, they are open inside the with context and closed outside.

This is using the Python context manager API.

EDIT: It seems like this already exists but is deprecated: See contextlib and this SO question. Use it like this:

import contextlib

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

It sounds like you are basically looking for contextlib.nested(), this was deprecated in Python 2.7 in favor of the multiple manager form of the with statement but as noted in the docs:

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

In case you are on Python 3.x, here is the code from the Python 2.7 source:

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]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top