Inhalt einer Datei mit Pickle laden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
stylish
User
Beiträge: 4
Registriert: Sonntag 8. Februar 2015, 16:16

Hallo,

Ich hab mich entschieden Python zu lernen und arbeite gerade das Python 3 Buch von Michael Weigend durch.
Nun habe ich ein Problem, dass ich nicht verstehe und hoffe, dass ich hier Hilfe finde.

In einer Aufgabe soll ich die Startnummer eines Langläufers sowie die momentane Zeit erfassen und diese in eine Datei speichern.

Das habe ich mit diesem Skript dargestellt:

Code: Alles auswählen

datei = open('daten/langlauf.txt', 'wb')
startnummer = input('startnummer: ')
import time
import pickle

while startnummer != "":
    zeit = time.asctime()
    speichern = [(startnummer), (zeit)]
    print(speichern)
    pickle.dump(speichern, datei)
    startnummer = input('startnummer: ')

datei.close()

In der While-Schleife habe ich print(speichern) eingebaut, weil ich sehen wollte, was denn gespeichtert wird. So wie ich das sehe, funktioniert das Skript.

Wenn ich die Datei aber mit einem anderen Skript öffnen will, bekomme ich nur Murks angezeigt und ich grübel schon den ganzen Tag warum. Ich weiß, dass ich das auch mit den Methoden .write, .flush und .close lösen könnte, aber ich möchte gerne das pickle Problem verstehen und mit pickle umgehen können.

Das Skript, welches Murks liefert, sieht so aus:

Code: Alles auswählen

import pickle
f = open('daten/langlauf.txt', 'rb')
datei = pickle.load(f)
f.close()
for zeiten in datei:
    print(zeiten[0], zeiten[1])
Vielen Dank!
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@stylish: was heißt murks?

noch ein paar Anmerkungen: imports kommen immer ganz oben in eine Python-Datei, Dateien öffnet man am besten mit dem with-Statement und für Datum und Zeit gibt es das datetime-Modul, das einen das Datum auch noch individuell formatieren läßt.
stylish
User
Beiträge: 4
Registriert: Sonntag 8. Februar 2015, 16:16

Wenn ich das Skirpt ausführe und beispielsweise folgendes eingebe:

startnummer: 12
['12', 'Sun Feb 8 16:31:03 2015']
startnummer: 23
['23', 'Sun Feb 8 16:31:05 2015']
startnummer: 45
['45', 'Sun Feb 8 16:31:07 2015']
startnummer:

erhalte ich mit meinem Skript dieses Ergebnis:

>>>
1 2
S u
>>>

Und das ist Murks.

Das mit den Imports werde ich mir merken. With-Statements kenne ich auch aber wie gesagt wollte ich die Aufgabe mit pickle lösen und gerne verstehen. Das mit der Datumsfunktion wurde bei der Aufgabenstellung so vorgegeben.
BlackJack

@stylish: Lass Dir doch mal den Wert von `datei` im zweiten Skript ausgeben.

Mit dem `load()` bekommst Du den ersten mit `dump()` gespeicherten Wert. Nicht mehr. Für mehr müsstest Du weitere `load()`-Aufrufe machen. Weshalb das aber eigentlich auch nicht der Weg ist den man normalerweise geht. Üblicherweise würde man erst alle Eingaben sammeln und dann *alles* mit *einem* `dump()`-Aufruf speichern. Dann kann man auch alles in einem `load()`-Aufruf wieder laden.

Sonstige Anmerkungen: Was hat denn ``with`` mit `pickle` zu tun? Wenn möglich sollte man immer ``with`` benutzen, und hier ist es eindeutig möglich.

Vermeide Code-Wiederholungen. Die Eingabe der Startnummer sollte nur an *einer* Stelle im Quelltext passieren. Dazu würde man eine Endlosschleife schreiben (``while True:``) und die dann mit ``break`` abbrechen wenn die Bedingung für die letzte Eingabe erfüllt wurde. In diesem Fall könnte man es noch einfacher haben und eine ``for``-Schleife mit der `iter()`-Funktion schreiben. Schau Dir mal an was man da für Argumente übergeben kann, ausser einem iterierbaren Objekt.

Die Klammern um `startnummer` und `zeit` in Zeile 8 sind überflüssig.

Wenn man schon `pickle` benutzt, dann sollte man das auch tatsächlich nutzen und Sachen dort speichern die man nicht auch mit `json` speichern könnte. Also zum Beispiel ein `datetime.datetime`-Objekt für die Zeit, statt einer Zeichenkette.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Und was erwartest Du?
Ich habe jetzt keine andere Ausgabe erwartet. Du liest das erste Pickelobjekt aus der Datei, die übrigens keine Text-Datei ist, wie die Dateiendung vermuten läßt. Das erste Pickel-Objekt ist eine Liste mit zwei Einträgen: startnummer und zeit. Dann gehst Du diese Liste mit for durch und gibst jeweils die ersten zwei Elemente, also die ersten zwei Buchstaben von startnummer und zeit aus.
stylish
User
Beiträge: 4
Registriert: Sonntag 8. Februar 2015, 16:16

Danke @Sirius3 und BlackJack

Mit euren Hinweisen habt ihr mir geholfen. Jetzt habe ich es hinbekommen:

Code: Alles auswählen

import pickle
f = open('daten/langlauf.txt', 'rb') 
datei = " "
while datei != "":
    try:
        datei = pickle.load(f)
        print(datei)
    except:
        break
f.close()
@BlackJack Habe es mit with versucht. Allerdings hatte sich das Skript dann in einer Endlosschleife verfangen. Wie würde die Lösung mit with aussehen?

@Sirius3 Warum ist die Datei eigentlich keine Text-Datei?
BlackJack

Die Schleife ist komisch wegen der Abbruchbedingung die eigentlich nicht wirklich benutzt wird. Denn die Schleife wird ja durch das ``break`` abgebrochen und nicht weil `datei` den Wert '' annehmen würde. `datei` ist auch ein schlechter Name, denn das ist kein Dateiobjekt.

Man sollte keine nackten ``except:``\s verwenden. Damit werden *alle* Ausnahmen behandelt, auch solche mit denen Du gar nicht gerechnet hast, und die werden dann in diesem Fall mit einem ``break`` behandelt und der Benutzer und auch Du als Programmierer bekommst dadurch überhaupt nicht mit wenn der Grund mal nicht das Ende der Datei sein sollte.

Code: Alles auswählen

import pickle


def main():
    with open('daten/langlauf.dat', 'rb') as data_file:
        try:
            while True:
                print(pickle.load(data_file))
        except EOFError:
            pass  # Intentionally ignored.


if __name__ == '__main__':
    main()
Wobei ich das immer noch für den falsche Weg halte da mehrere Serialisierungen nacheinander in die Datei zu schreiben.

Warum die Datei keine Textdatei ist? Nun weil das eben so definiert wurde. Wenn Du Pickle-Dateien als Textdateien behandelst, auch die Protokollversionen die sich auf Bytewerte im ASCII-Wertebereich beschränken, dann ist das undefiniertes Verhalten und es gibt diverse Variationen/Situationen wo das dann einfach nicht funktioniert.
stylish
User
Beiträge: 4
Registriert: Sonntag 8. Februar 2015, 16:16

Vielen Dank BlackJack. Hast mir und meinem Verständnis sehr weitergeholfen.
Antworten