Откройте список файлов с помощью / в качестве диспетчера контекста

Примечание. Мне известно о

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

синтаксис. Это другой вопрос.


Учитывая список строк file_names, есть ли способ с помощью _3 _ / _ 4_ открывать каждое имя файла в нем, используя одну строку. Что-то вроде:

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

что, конечно, не работает, поскольку он пытается использовать диспетчер контекста в списке. Длина списка может быть неизвестна до времени выполнения, например sys.argv[1:]


person Ryan Haining    schedule 16.10.2013    source источник
comment
вы можете написать свой собственный менеджер контекста. это вариант? это довольно просто. docs.python.org/release/2.5.1/ref/ context-manager.html   -  person Corley Brigman    schedule 16.10.2013


Ответы (3)


Если у вас есть доступ к Python 3.3+, существует специальный класс, разработанный именно для этой цели: _ 1_. Это работает так, как вы ожидаете:

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
person Henry Keiter    schedule 16.10.2013

Как насчет этого?

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 и закрыты снаружи.

Для этого используется API диспетчера контекста Python.

РЕДАКТИРОВАТЬ: похоже, что это уже существует, но устарело: см. contextlib и этот вопрос SO < / а>. Используйте это так:

import contextlib

with contextlib.nested(*[open(fn, 'w') for fn in arr]) as files:
    print files
print files
person Max    schedule 16.10.2013
comment
contextlib.nested устарел - person Ryan Haining; 17.10.2013
comment
Спасибо, обновился. ListContext, который я привожу здесь, имеет те же предостережения ... т.е. проблемы возникают, если __enter__() или __exit__() внутренних элементов вызывают исключения. - person Max; 17.10.2013

Похоже, вы в основном ищете 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]
person Andrew Clark    schedule 16.10.2013
comment
Невозможно использовать форму нескольких менеджеров оператора with со списком менеджеров контекста произвольной длины. - person cowlinator; 13.11.2019