Seite 1 von 1

Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 16:33
von stylish
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!

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 17:00
von Sirius3
@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.

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 17:23
von stylish
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.

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 17:28
von 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.

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 17:29
von Sirius3
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.

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 19:00
von stylish
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?

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 19:13
von 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.

Re: Inhalt einer Datei mit Pickle laden

Verfasst: Sonntag 8. Februar 2015, 19:26
von stylish
Vielen Dank BlackJack. Hast mir und meinem Verständnis sehr weitergeholfen.