Python - write () versus writelines () und verkettete Strings

124

Also lerne ich Python. Ich gehe die Lektionen durch und bin auf ein Problem gestoßen, bei dem ich sehr viele target.write()zu einer einzigen zusammenfassen musste write(), während ich "\n"zwischen jeder Benutzereingabevariable (das Objekt von write()) eine hatte.

Ich hatte die Idee dass:

nl = "\n"
lines = line1, nl, line2, nl, line3, nl
textdoc.writelines(lines)

Wenn ich versuche zu tun:

textdoc.write(lines)

Ich bekomme eine Fehlermeldung. Aber wenn ich tippe:

textdoc.write(line1 + "\n" + line2 + ....)

Dann funktioniert es gut. Warum kann ich keinen String für eine neue Zeile verwenden, write()aber ich kann ihn verwenden writelines()?

Python 2.7 Als ich bei Google nach den meisten Ressourcen gesucht habe, die ich gefunden habe, bin ich immer noch ein Laie.

AbeLinkon
quelle
linesist in Ihrem Beispiel keine Zeichenfolge. Es ist ein Tupel, das aus sechs Saiten besteht.
Bachsau

Antworten:

147
  • writelines erwartet eine Iteration von Strings
  • write erwartet eine einzelne Zeichenfolge.

line1 + "\n" + line2führt diese Zeichenfolgen zu einer einzelnen Zeichenfolge zusammen, bevor sie an übergeben werden write.

Beachten Sie, dass Sie möglicherweise verwenden möchten, wenn Sie viele Zeilen haben "\n".join(list_of_lines).

DGH
quelle
50
Insbesondere writelineserwartet eine iterable. Sie können eine Liste, ein Tupel oder einen Generator verwenden.
Mark Ransom
Vielen Dank für die Antwort, Sir. Ich gehe mit dem Namen (list_of_lines) davon aus, dass ich eine Liste von Strings erstellen und dann an .join (list) übergeben soll.
AbeLinkon
9
Warum sollten Sie writeanstelle von verwenden, writelineswenn Sie viele Zeilen haben? Writelines könnten eine bessere Leistung erzielen, da sie keine temporäre verkettete Zeichenfolge erstellen müssen, sondern nur über die Zeilen iterieren.
Bouke
@ hBy2Py: genau das Gegenteil: stackoverflow.com/a/6165711/281545
Mr_and_Mrs_D
1
Eine einzelne Zeichenfolge ist auch in Python
Natbusa
122

Warum kann ich in write () keine Zeichenfolge für eine neue Zeile verwenden, aber in writelines ()?

Die Idee ist folgende: Wenn Sie eine einzelne Zeichenfolge schreiben möchten, können Sie dies tun write(). Wenn Sie eine Folge von Zeichenfolgen haben, können Sie diese alle mit schreiben writelines().

write(arg)erwartet einen String als Argument und schreibt ihn in die Datei. Wenn Sie eine Liste von Zeichenfolgen angeben, wird eine Ausnahme ausgelöst (zeigen Sie uns übrigens Fehler!).

writelines(arg)erwartet ein iterierbares Argument (ein iterierbares Objekt kann im allgemeinsten Sinne ein Tupel, eine Liste, eine Zeichenfolge oder ein Iterator sein). Es wird erwartet, dass jedes im Iterator enthaltene Element eine Zeichenfolge ist. Ein Tupel von Zeichenfolgen ist das, was Sie bereitgestellt haben, also haben die Dinge funktioniert.

Die Art der Zeichenfolge (n) spielt für beide Funktionen keine Rolle, dh sie schreiben einfach in die Datei, was auch immer Sie ihnen zur Verfügung stellen. Der interessante Teil ist, dass writelines()keine Zeilenumbruchzeichen hinzugefügt werden, sodass der Methodenname tatsächlich ziemlich verwirrend sein kann. Es verhält sich tatsächlich wie eine imaginäre Methode namens write_all_of_these_strings(sequence).

Was folgt, ist eine idiomatische Methode in Python, um eine Liste von Zeichenfolgen in eine Datei zu schreiben, während jede Zeichenfolge in einer eigenen Zeile bleibt:

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.write('\n'.join(lines))

Dadurch wird die Datei für Sie geschlossen. Das Konstrukt '\n'.join(lines)verkettet (verbindet) die Zeichenfolgen in der Liste linesund verwendet das Zeichen '\ n' als Klebstoff. Es ist effizienter als die Verwendung des +Operators.

Ausgehend von derselben linesSequenz, endend mit derselben Ausgabe, aber mit writelines():

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.writelines("%s\n" % l for l in lines)

Dies verwendet einen Generatorausdruck und erstellt dynamisch Zeichenfolgen mit Zeilenumbruch. writelines()iteriert über diese Folge von Zeichenfolgen und schreibt jedes Element.

Bearbeiten: Ein weiterer Punkt, den Sie beachten sollten:

write()und readlines()existierte, bevor writelines()eingeführt wurde. writelines()wurde später als Gegenstück zu eingeführt readlines(), so dass man den Dateiinhalt, der gerade gelesen wurde, leicht schreiben konnte über readlines():

outfile.writelines(infile.readlines())

Wirklich, das ist der Hauptgrund, warum writelinesso ein verwirrender Name hat. Auch heute wollen wir diese Methode nicht mehr wirklich anwenden. readlines()Liest die gesamte Datei in den Speicher Ihres Computers, bevor writelines()mit dem Schreiben der Daten begonnen wird. Dies kann zuallererst Zeit verschwenden. Warum nicht anfangen, Teile von Daten zu schreiben, während Sie andere Teile lesen? Vor allem aber kann dieser Ansatz sehr speicherintensiv sein. In einem extremen Szenario, in dem die Eingabedatei größer als der Speicher Ihres Computers ist, funktioniert dieser Ansatz nicht einmal. Die Lösung für dieses Problem besteht darin, nur Iteratoren zu verwenden. Ein Arbeitsbeispiel:

with open('inputfile') as infile:
    with open('outputfile') as outfile:
        for line in infile:
            outfile.write(line)

Dies liest die Eingabedatei Zeile für Zeile. Sobald eine Zeile gelesen wurde, wird diese Zeile in die Ausgabedatei geschrieben. Schematisch gesehen befindet sich immer nur eine einzige Zeile im Speicher (im Vergleich zum gesamten Dateiinhalt im Speicher, wenn der Readlines / Writelines-Ansatz verwendet wird).

Dr. Jan-Philip Gehrcke
quelle
5
@ BeLinkon: Ich würde diese Schlussfolgerung nicht unterstützen. write()und writelines()sind grundsätzlich gleichwertig und ihre Verwendung ist auch eine Frage des persönlichen Geschmacks. Es ist jedoch wichtig zu beachten, dass für eine extrem lange Liste von Zeichenfolgen (aufgerufen lines) das Schreiben weniger effizient ist f.write('\n'.join(lines))als für for l in line: f.write('%s\n' % l). Im ersten Fall wird vor dem Schreiben eine völlig neue und sehr lange Zeichenfolge im Speicher erstellt. Im zweiten Fall werden die Daten stückweise geschrieben.
Dr. Jan-Philip Gehrcke
3
f.write ('\ n'.join (Zeilen)) hat das letzte nl nicht hinzugefügt, als ich es ausgeführt habe.
Jiminion
5
Natürlich würdest du es nicht tun, outf.writelines(inf.readlines())sondern lieber outf.writelines(inf). Die Funktion, die wir nicht mehr verwenden möchten, ist readlines()nicht writelines().
Moooeeeep
2
@moooeeeep: Während nichts an der Funktionalität / Implementierung von falsch ist writelines(), ist seine Semantik, wie erklärt, weniger als ideal. Deshalb habe ich es nie benutzt. Und ich habe es nie verpasst.
Dr. Jan-Philip Gehrcke
2
@ BeLinkon - vielleicht sollten Sie überlegen, diese Antwort zu akzeptieren, es ist eindeutig besser als die, die Sie ursprünglich akzeptiert haben
Peter M. - steht für Monica
-4

Wenn Sie nur eine Liste speichern und laden möchten, versuchen Sie es mit Pickle

Essiggurken sparen:

with open("yourFile","wb")as file:
 pickle.dump(YourList,file)

und Laden:

with open("yourFile","rb")as file:
 YourList=pickle.load(file)
Venya
quelle
-5

Eigentlich denke ich, dass das Problem ist, dass Ihre Variablen "Zeilen" schlecht sind. Sie haben Zeilen als Tupel definiert, aber ich glaube, dass write () eine Zeichenfolge erfordert. Sie müssen lediglich Ihre Kommas in Pluszeichen (+) ändern.

nl = "\n"
lines = line1+nl+line2+nl+line3+nl
textdoc.writelines(lines)

sollte arbeiten.

Kevin
quelle
-5

Übung 16 aus Zed Shaws Buch? Sie können Escape-Zeichen wie folgt verwenden:

paragraph1 = "%s \n %s \n %s \n" % (line1, line2, line3)
target.write(paragraph1)
target.close()
Gerald
quelle
Sehr schwache Lösung. Wenn Sie mehrere Zeilen auf diese Weise verketten möchten, sollten Sie dies folgendermaßen tun : " \n ".join((line1, line2, line3)).
Bachsau