Откройте список файлов, используя/в качестве менеджера контекста
Примечание. Я знаю, что
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:]
Ответы
Ответ 1
Если у вас есть доступ к 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
Ответ 2
Как насчет этого?
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>]
Обратите внимание: они открыты внутри контекста и закрыты снаружи.
Это использует API-интерфейс Python API-интерфейса контекста.
EDIT: похоже, что это уже существует, но устарело: см. contextlib и этот вопрос SO. Используйте его так:
import contextlib
with contextlib.nested(*[open(fn, 'w') for fn in arr]) as files:
print files
print files
Ответ 3
Похоже, вы в основном ищете contextlib.nested()
, это было устарело в Python 2.7 в пользу формы с несколькими менеджерами with, но как указано в документах:
Единственное преимущество этой функции над формой нескольких менеджеров оператора for заключается в том, что распаковка аргументов позволяет использовать ее с переменным числом менеджеров контекста.
Если вы находитесь на 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]