So öffnen Sie eine Datei mit der Anweisung open with

200

Ich schaue mir an, wie man Dateien in Python ein- und ausgibt. Ich habe den folgenden Code geschrieben, um eine Liste von Namen (einen pro Zeile) aus einer Datei in eine andere Datei zu lesen, während ein Name mit den Namen in der Datei verglichen und Text an die Vorkommen in der Datei angehängt wird. Der Code funktioniert. Könnte es besser gemacht werden?

Ich wollte die with open(...Anweisung sowohl für Eingabe- als auch für Ausgabedateien verwenden, kann aber nicht sehen, wie sie sich im selben Block befinden könnten, was bedeutet, dass ich die Namen an einem temporären Speicherort speichern müsste.

def filter(txt, oldfile, newfile):
    '''\
    Read a list of names from a file line by line into an output file.
    If a line begins with a particular name, insert a string of text
    after the name before appending the line to the output file.
    '''

    outfile = open(newfile, 'w')
    with open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

    outfile.close()
    return # Do I gain anything by including this?

# input the name you want to check against
text = input('Please enter the name of a great person: ')    
letsgo = filter(text,'Spanish', 'Spanish2')
Disnami
quelle
"Das heißt, ich müsste die Namen an einem temporären Ort speichern"? Können Sie erklären, was Sie damit meinen?
S.Lott
4
Beachten Sie, dass filter()ist eine integrierte Funktion und so werden Sie wahrscheinlich einen anderen Namen für Ihre Funktion wählen sollten.
Tom
2
@ Tom Überschreibt eine Funktion im Namespace die integrierte Funktion?
UpTide
2
@UpTide: Ja, Python arbeitet in LEGB-Reihenfolge - lokal, einschließend, global, integriert (siehe stackoverflow.com/questions/291978/… ). Wenn Sie also eine globale Funktion ( filter()) erstellen, wird diese vor der integrierten Funktion gefundenfilter()
Tom

Antworten:

308

Python ermöglicht das Einfügen mehrerer open()Anweisungen in eine einzige with. Sie trennen sie durch Kommas. Ihr Code wäre dann:

def filter(txt, oldfile, newfile):
    '''\
    Read a list of names from a file line by line into an output file.
    If a line begins with a particular name, insert a string of text
    after the name before appending the line to the output file.
    '''

    with open(newfile, 'w') as outfile, open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

# input the name you want to check against
text = input('Please enter the name of a great person: ')    
letsgo = filter(text,'Spanish', 'Spanish2')

Und nein, Sie gewinnen nichts, wenn Sie returnam Ende Ihrer Funktion ein explizites Zeichen setzen . Sie können verwenden return, um vorzeitig zu beenden, aber Sie hatten es am Ende, und die Funktion wird ohne es beendet. (Bei Funktionen, die einen Wert zurückgeben, geben Sie natürlich returnden Wert an, der zurückgegeben werden soll.)

Die Verwendung mehrerer open()Elemente mit withwurde in Python 2.5 bei withEinführung der Anweisung oder in Python 2.6 nicht unterstützt , wird jedoch in Python 2.7 und Python 3.1 oder höher unterstützt.

http://docs.python.org/reference/compound_stmts.html#the-with-statement http://docs.python.org/release/3.1/reference/compound_stmts.html#the-with-statement

Wenn Sie Code schreiben, der in Python 2.5, 2.6 oder 3.0 ausgeführt werden muss, verschachteln Sie die withAnweisungen wie in den anderen vorgeschlagenen oder verwendeten Antworten contextlib.nested.

steveha
quelle
28

Verwenden Sie verschachtelte Blöcke wie folgt:

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        # your logic goes right here
RanRag
quelle
12

Sie können Ihre mit Blöcken verschachteln. So was:

with open(newfile, 'w') as outfile:
    with open(oldfile, 'r', encoding='utf-8') as infile:
        for line in infile:
            if line.startswith(txt):
                line = line[0:len(txt)] + ' - Truly a great person!\n'
            outfile.write(line)

Dies ist besser als Ihre Version, da Sie garantieren, dass outfilediese geschlossen wird, auch wenn Ihr Code auf Ausnahmen stößt. Natürlich könnten Sie das mit try / finally tun, aber es withist der richtige Weg, dies zu tun.

Oder, wie ich gerade erfahren habe, können Sie mehrere Kontextmanager in einer with-Anweisung haben, wie von @steveha beschrieben . Das scheint mir eine bessere Option zu sein als das Verschachteln.

Und für Ihre letzte kleine Frage hat die Rückgabe keinen wirklichen Zweck. Ich würde es entfernen.

David Heffernan
quelle
Vielen Dank. Ich werde es versuchen und Ihre Antwort akzeptieren, wenn ich es zum Laufen bringe.
Disnami
Danke noch einmal. Ich muss sieben Minuten warten, bevor ich akzeptieren kann.
Disnami
7
@Disnami stellen Sie sicher, dass Sie die richtige Antwort akzeptieren (und es ist nicht diese!) ;-)
David Heffernan
1

Manchmal möchten Sie möglicherweise eine variable Anzahl von Dateien öffnen und jede gleich behandeln. Sie können dies tun contextlib

from contextlib import ExitStack
filenames = [file1.txt, file2.txt, file3.txt]

with open('outfile.txt', 'a') as outfile:
    with ExitStack() as stack:
        file_pointers = [stack.enter_context(open(file, 'r')) for file in filenames]                
            for fp in file_pointers:
                outfile.write(fp.read())                   
Bruder-Bilo
quelle