Как я могу открыть несколько файлов, используя «с открытым» в Python?

672

Я хочу изменить пару файлов одновременно, если я смогу записать их все. Мне интересно, смогу ли я как-то объединить несколько открытых вызовов с помощью withоператора:

try:
  with open('a', 'w') as a and open('b', 'w') as b:
    do_something()
except IOError as e:
  print 'Operation failed: %s' % e.strerror

Если это невозможно, как будет выглядеть элегантное решение этой проблемы?

Frantischeck003
источник

Ответы:

1052

Начиная с Python 2.7 (или 3.1 соответственно) вы можете написать

with open('a', 'w') as a, open('b', 'w') as b:
    do_something()

В более ранних версиях Python иногда можно использовать contextlib.nested()для вложения контекстных менеджеров. Это не будет работать, как ожидается, для открытия нескольких файлов - подробности смотрите в связанной документации.


В редком случае, когда вы хотите открыть переменное количество файлов одновременно, вы можете использовать contextlib.ExitStack, начиная с Python версии 3.3:

with ExitStack() as stack:
    files = [stack.enter_context(open(fname)) for fname in filenames]
    # Do something with "files"

Однако в большинстве случаев у вас есть переменный набор файлов, но вы, вероятно, захотите открывать их один за другим.

Свен Марнах
источник
5
К сожалению, в соответствии с документами contextlib.nested, вы не должны использовать его для открытия файла: «использование nested () для открытия двух файлов является ошибкой программирования, поскольку первый файл не будет закрываться сразу, если при открытии возникнет исключение второй файл. "
Вероника
41
Есть ли способ использовать, withчтобы открыть список переменных файлов?
Monkut
23
@monkut: Очень хороший вопрос (вы могли бы задать его как отдельный вопрос). Краткий ответ: да, есть ExitStackкак на Python 3.3. Нет простого способа сделать это в любой более ранней версии Python.
Свен Марнах
12
Возможно ли, чтобы этот синтаксис занимал несколько строк?
tommy.carstensen
9
@ tommy.carstensen: Вы можете использовать обычные механизмы продолжения строки . Вам, вероятно, следует использовать продолжение строки с обратной косой чертой, чтобы разбить запятую, как рекомендовано в PEP 9 .
Свен Марнач
99

Просто замените andна ,и все готово:

try:
    with open('a', 'w') as a, open('b', 'w') as b:
        do_something()
except IOError as e:
    print 'Operation failed: %s' % e.strerror
Майкл
источник
3
Вы должны указать, какие версии Python поддерживают этот синтаксис.
Крейг МакКуин
58

Для одновременного открытия большого количества файлов или для длинных путей к файлам может быть полезно разбить элементы на несколько строк. Из Руководства по стилю Python, предложенного @Sven Marnach в комментариях к другому ответу:

with open('/path/to/InFile.ext', 'r') as file_1, \
     open('/path/to/OutFile.ext', 'w') as file_2:
    file_2.write(file_1.read())
Майкл Олрогге
источник
1
С этим отступом я получаю: «flake8: строка продолжения заштрихована для визуального отступа»
Louis M
@LouisM Это звучит как что-то из вашего редактора или окружения, а не из базового питона. Если это по-прежнему будет проблемой для вас, я бы порекомендовал создать новый вопрос, касающийся этого и дать более подробную информацию о вашем редакторе и среде.
Майкл Олрогге
3
Да, это определенно мой редактор, и это только предупреждение. Я хотел подчеркнуть, что ваши отступы не соответствуют PEP8. Вы должны сделать отступ во втором open () с 8 пробелами, а не выравнивать его с первым.
Луи М
2
@LouisM PEP8 - это руководство , а не правила, и в этом случае я бы наверняка проигнорировал его
Ник
2
Да, нет проблем с этим, хотя это может быть полезно для других людей с автоматическим линтером :)
Louis M
15

Вложенные с заявлениями сделают ту же самую работу, и, на мой взгляд, более просты в обращении.

Допустим, у вас есть inFile.txt, и вы хотите записать его в два outFile одновременно.

with open("inFile.txt", 'r') as fr:
    with open("outFile1.txt", 'w') as fw1:
        with open("outFile2.txt", 'w') as fw2:
            for line in fr.readlines():
                fw1.writelines(line)
                fw2.writelines(line)

РЕДАКТИРОВАТЬ:

Я не понимаю причину понижения. Я проверил свой код перед публикацией своего ответа, и он работает, как нужно: пишет во все outFile, как и спрашивает вопрос. Нет дублирования записи или не писать. Поэтому мне действительно любопытно узнать, почему мой ответ считается неправильным, неоптимальным или чем-то подобным.

FatihAkici
источник
1
я не знаю, что кто-то еще осудил тебя, но я упросил тебя, потому что это единственный пример, в котором было три файла (один вход, два выхода), который оказался именно тем, что мне было нужно.
Адам Майкл Вуд
2
может быть, вы пропустили bcoz в Python> 2.6, вы можете написать больше кода на pythonic - gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8 (почему я не могу вставить фрагмент кода в комментарии ?!) Мы находимся в 2018 году; прошлое
Эль Русо
2
Дружеское напоминание тем пугающим питонам 2.6: CentOS 6 (который не EOL до ноября 2020 года), по-прежнему использует py2.6 по умолчанию. Таким образом, этот ответ (на данный момент) по-прежнему лучший в целом ИМО.
BJ Black
11

Начиная с Python 3.3, вы можете использовать класс ExitStackиз contextlibмодуля, чтобы безопасно
открывать произвольное количество файлов .

Он может управлять динамическим числом контекстно-зависимых объектов, что означает, что он окажется особенно полезным, если вы не знаете, сколько файлов вы собираетесь обрабатывать .

Фактически, канонический вариант использования, упомянутый в документации, управляет динамическим числом файлов.

with 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

Если вы заинтересованы в деталях, вот общий пример, чтобы объяснить, как ExitStackработает:

from contextlib import ExitStack

class X:
    num = 1

    def __init__(self):
        self.num = X.num
        X.num += 1

    def __repr__(self):
        cls = type(self)
        return '{cls.__name__}{self.num}'.format(cls=cls, self=self)

    def __enter__(self):
        print('enter {!r}'.format(self))
        return self.num

    def __exit__(self, exc_type, exc_value, traceback):
        print('exit {!r}'.format(self))
        return True

xs = [X() for _ in range(3)]

with ExitStack() as stack:
    print(len(stack._exit_callbacks)) # number of callbacks called on exit
    nums = [stack.enter_context(x) for x in xs]
    print(len(stack._exit_callbacks))

print(len(stack._exit_callbacks))
print(nums)

Вывод:

0
enter X1
enter X2
enter X3
3
exit X3
exit X2
exit X1
0
[1, 2, 3]
timgeb
источник
3

С Python 2.6 это не будет работать, мы должны использовать следующий способ, чтобы открыть несколько файлов:

with open('a', 'w') as a:
    with open('b', 'w') as b:
Аашутош Джа
источник
1

Поздний ответ (8 лет), но для тех, кто хочет объединить несколько файлов в один , может помочь следующая функция:

def multi_open(_list):
    out=""
    for x in _list:
        try:
            with open(x) as f:
                out+=f.read()
        except:
            pass
            # print(f"Cannot open file {x}")
    return(out)

fl = ["C:/bdlog.txt", "C:/Jts/tws.vmoptions", "C:/not.exist"]
print(multi_open(fl))

2018-10-23 19:18:11.361 PROFILE  [Stop Drivers] [1ms]
2018-10-23 19:18:11.361 PROFILE  [Parental uninit] [0ms]
...
# This file contains VM parameters for Trader Workstation.
# Each parameter should be defined in a separate line and the
...
CONvid19
источник