Wie kann ich mehrere Dateien mit "with open" in Python öffnen?

670

Ich möchte ein paar Dateien gleichzeitig ändern, wenn ich in alle schreiben kann. Ich frage mich, ob ich die mehreren offenen Anrufe irgendwie mit der withAussage kombinieren kann :

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

Wenn dies nicht möglich ist, wie würde eine elegante Lösung für dieses Problem aussehen?

Frantischeck003
quelle

Antworten:

1050

Ab Python 2.7 (bzw. 3.1) können Sie schreiben

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

In früheren Versionen von Python können Sie manchmal contextlib.nested()Kontextmanager verschachteln. Dies funktioniert jedoch nicht wie erwartet beim Öffnen mehrerer Dateien. Weitere Informationen finden Sie in der verknüpften Dokumentation.


In dem seltenen Fall, dass Sie eine variable Anzahl von Dateien gleichzeitig öffnen möchten, können Sie contextlib.ExitStackab Python Version 3.3 Folgendes verwenden:

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

In den meisten Fällen, in denen Sie einen variablen Satz von Dateien haben, möchten Sie diese wahrscheinlich nacheinander öffnen.

Sven Marnach
quelle
5
Leider sollten Sie es laut den Dokumenten contextlib.nested nicht zum Öffnen von Dateien verwenden: "Die Verwendung von nested () zum Öffnen von zwei Dateien ist ein Programmierfehler, da die erste Datei nicht sofort geschlossen wird, wenn beim Öffnen der Datei eine Ausnahme ausgelöst wird zweite Datei. "
Weronika
41
Gibt es eine Möglichkeit, witheine variable Liste von Dateien zu öffnen?
Mönch
23
@monkut: Sehr gute Frage (Sie könnten dies tatsächlich als separate Frage stellen). Kurze Antwort: Ja, es gibt ExitStackab Python 3.3. In früheren Versionen von Python gibt es keine einfache Möglichkeit, dies zu tun.
Sven Marnach
12
Ist es möglich, dass diese Syntax mehrere Zeilen umfasst?
Tommy.carstensen
9
@ tommy.carstensen: Sie können die üblichen Leitungsfortsetzungsmechanismen verwenden . Sie sollten wahrscheinlich die Fortsetzung der Backslash-Zeile verwenden, um das Komma zu unterbrechen, wie von PEP 9 empfohlen .
Sven Marnach
99

Ersetzen Sie einfach andmit ,und Sie sind fertig:

try:
    with open('a', 'w') as a, open('b', 'w') as b:
        do_something()
except IOError as e:
    print 'Operation failed: %s' % e.strerror
Michael
quelle
3
Sie sollten angeben, welche Python-Versionen diese Syntax unterstützen.
Craig McQueen
58

Zum Öffnen vieler Dateien gleichzeitig oder für lange Dateipfade kann es hilfreich sein, die Dinge über mehrere Zeilen aufzuteilen. Aus dem Python Style Guide, wie von @Sven Marnach in Kommentaren zu einer anderen Antwort vorgeschlagen:

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())
Michael Ohlrogge
quelle
1
Mit diesem Einzug bekomme ich: "flake8: Fortsetzungslinie für visuellen Einzug über eingerückt"
Louis M
@LouisM Das klingt eher nach etwas, das von Ihrem Editor oder Ihrer Umgebung kommt, als nach Basis-Python. Wenn es weiterhin ein Problem für Sie ist, würde ich empfehlen, eine neue Frage zu erstellen und mehr Details zu Ihrem Editor und Ihrer Umgebung zu geben.
Michael Ohlrogge
3
Ja, es ist definitiv mein Editor und es ist nur eine Warnung. Was ich betonen wollte, ist, dass Ihre Einrückung nicht mit PEP8 übereinstimmt. Sie sollten das zweite open () mit 8 Leerzeichen einrücken, anstatt es mit dem ersten auszurichten.
Louis M
2
@ LouisM PEP8 ist eine Richtlinie , keine Regeln, und in diesem Fall würde ich es mit Sicherheit ignorieren
Nick
2
Ja, kein Problem damit, es könnte für andere Leute mit automatischen Lintern nützlich sein :)
Louis M
14

Mit Aussagen verschachtelte Aufgaben erledigen den gleichen Job und sind meiner Meinung nach einfacher zu handhaben.

Angenommen, Sie haben inFile.txt und möchten es gleichzeitig in zwei outFiles schreiben.

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)

BEARBEITEN:

Ich verstehe den Grund der Ablehnung nicht. Ich habe meinen Code getestet, bevor ich meine Antwort veröffentlicht habe, und er funktioniert wie gewünscht: Er schreibt in alle outFiles, genau wie es die Frage verlangt. Kein doppeltes Schreiben oder Nichtschreiben. Ich bin wirklich neugierig zu wissen, warum meine Antwort als falsch, suboptimal oder so angesehen wird.

FatihAkici
quelle
1
Ich weiß nicht, was dich jemand anderes herabgestimmt hat, aber ich habe dich aufgewertet, weil dies das einzige Beispiel ist, das drei Dateien (eine Eingabe, zwei Ausgaben) hatte, die genau das waren, was ich brauchte.
Adam Michael Wood
2
Vielleicht sind Sie bcoz in Python> 2.6 herabgestuft. Sie können mehr Python-Code schreiben - gist.github.com/IaroslavR/3d8692e2a11e1ef902d2d8277eb88cb8 (warum kann ich kein Codefragment in die Kommentare einfügen ?! ) Wir sind im Jahr 2018;) so alte Versionen in die Vergangenheit
El Ruso
2
Eine freundliche Erinnerung an diese poo-poohing Python 2.6: CentOS 6 (die erst im November 2020 EOL wird) verwendet standardmäßig immer noch py2.6. Daher ist diese Antwort (derzeit) immer noch die beste IMO insgesamt.
BJ Black
11

Seit Python 3.3 können Sie die Klasse ExitStackaus dem contextlibModul verwenden,
um eine beliebige Anzahl von Dateien sicher zu öffnen .

Es kann eine dynamische Anzahl kontextsensitiver Objekte verwalten. Dies bedeutet, dass es sich als besonders nützlich erweist, wenn Sie nicht wissen, wie viele Dateien Sie verarbeiten werden .

Tatsächlich verwaltet der in der Dokumentation erwähnte kanonische Anwendungsfall eine dynamische Anzahl von Dateien.

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

Wenn Sie an den Details interessiert sind, finden Sie hier ein allgemeines Beispiel, um zu erklären, wie es ExitStackfunktioniert:

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)

Ausgabe:

0
enter X1
enter X2
enter X3
3
exit X3
exit X2
exit X1
0
[1, 2, 3]
timgeb
quelle
3

Mit Python 2.6 wird es nicht funktionieren, wir müssen die folgende Methode verwenden, um mehrere Dateien zu öffnen:

with open('a', 'w') as a:
    with open('b', 'w') as b:
Aashutosh jha
quelle
1

Späte Antwort (8 Jahre), aber für jemanden, der mehrere Dateien zu einer zusammenfügen möchte , kann die folgende Funktion hilfreich sein:

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
quelle