Version 2.7 / 3.3 Verständnisprobleme von pickle

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
Hero2
User
Beiträge: 19
Registriert: Freitag 16. April 2010, 20:13

Hallo,
ich will euch mal hier ein kleines Programm zeigen, was im Aktuellen Verzeichnis alle .txt einliest und dann ein Wörterbuch erstellt, wo die Wörter der Dateien die Values sind und die die Namen der Dateien die Keys.

Code: Alles auswählen

import pickle
from os import listdir
import re
import pickle


try:
  with open("picklesp.pickle","r") as speicherort:
    Diginari = pickle.load(speicherort)
except IOError as err:
  Diginari = {}
except pickle.PickleError as p_error:
  print("Pickle Fehler : ", p_error)


alle_Datein = [Datei for Datei in listdir(".") if ".txt" in Datei and ".txt~"  not in Datei]

for daten in alle_Datein:
    try:
        with open(daten,"r") as aktuelle_datei:
          tet = re.split("\s",aktuelle_datei.read())
          for woerter in tet:
            if (len(woerter) == 0):
              pass
            elif (woerter not in Diginari.keys()):
              Diginari[woerter] = [daten]
            elif (woerter in Diginari.keys()):
              if (daten not in Diginari[woerter]):
                Diginari[woerter].append(daten)
    
                
    except IOError as err:
        print("Dateifehler ist Aufgetreten.\nEs ist folgender Fehler: ",err," in der Datei : ",daten)
       
print(Diginari)

try:
  with open("picklesp.pickle","w") as speicherort:
    pickle.dump(Diginari,speicherort)
except IOError as err:
  print("Dateifehler ist beim Speichern Aufgatereten.\nEs ist folgender Fehler: ",err)
except pickle.PickleError as p_error:
  print("Pickle Fehler : ", p_error)
Das Programm geht unter 2.7 super. In der Uni benutzen wir die 2.7 Version. Zuhause hab ich aber die 3.3 Version und wollte das Programm nun ausführen. Ich kriege aber pickle nicht unter Kontrolle in der 3.3 Version und weiß nur so halb warum.

Die Lösung war, dass ich die pickle datei löschen müsste. aber warum das ganze ?

Für die 3.3 Version hab ich nun folgendes verändert und so geht er auch in der Version 3.3 :

Code: Alles auswählen

try:
  with open("picklesp.pickle","[b]rb[/b]") as speicherort:
    Diginari = pickle.load(speicherort)
except IOError as err:
  Diginari = {}
except pickle.PickleError as p_error:
  print("Pickle Fehler : ", p_error)

#... code ist gleich mit oben .....

try:
  with open("picklesp.pickle","[b]wb[/b]") as speicherort:
    pickle.dump(Diginari,speicherort)
except IOError as err:
  print("Dateifehler ist beim Speichern Aufgatereten.\nEs ist folgender Fehler: ",err)
except pickle.PickleError as p_error:
  print("Pickle Fehler : ", p_error)
Das Problem ist nur, dass ich nicht verstehe, warum ich open("picklesp.pickle","wb") in der 3.3 Version machen muss. Kann mir das nochmal einer bitte erklären. Ich hab das hier :
http://docs.python.org/3.3/library/pickle.html nicht so ganz verstanden und verstehe den Unterschied nicht wirklich von 2.7 und 3.3 und warum das vor allem so gemacht wurde.

Eine zweite Frage zu pickle ist : Ist es möglich ein gepicklete Datei, die in der Version 2.7 entstanden ist in die Version 3.3 einzulesen?

Eine andere Sache ist : Habt ihr noch Verbesserungsvorschläge für den Code ? Was haltet ihr vom Code ?
Was ist besser, diese from bla import x, oder import bla nur ?

Ps.: Der Code ist glaub ich gut verständlich und deswegen nicht groß kommentiert.

Mit freundlichen Grüßen
BlackJack

@Hero2: Pickles sind Binärdateien und Du musst in jeder Python-Version die Datei im Binärmodus öffnen, damit die entstehenden Dateien auch sicher überall wieder eingelesen werdne können. Und zwar Binärmodus beim Lesen und Schreiben.

Zum Programm: Ein Modul mehrfach zu importieren macht keinen Sinn.

Der Quelltext hält sich nicht an den PEP 8 -- Style Guide for Python Code was Einrücktiefe und Namensschreibweisen angeht.

Ich bin mir recht sicher dass die Prüfung bei den Dateinamen nicht das ist was Du eigentlich willst, denn '.txt' ist auch in 'spam.txt.bmp' enthalten. Du möchtest vielleicht testen ob der Dateiname mit '.txt' *endet* und nicht ob es irgendwo im Namen vorkommt. Eventuell ist auch das `glob`-Modul an der Stelle praktischer.

Die Namen sind teilweise schlecht gewählt. `Diginari` sagt mir erst einmal nichts. `alle_Datein` ist orthographisch falsch und vom Sinn her enthält es weder Dateien noch alle, sondern Dateinamen und zwar die von (höchstwahrscheinlich) Textdateien. `daten` ist kein passender Name für einzelne Dateinamen. Und den Namen `woerter` für *einzelne* Namen zu verwenden ist irreführend.

Um Bedingungen muss man keine unnötigen Klammern setzen.

Ein Entscheidungszweig der nur ein ``pass`` enthält ist überflüssig.

`Diginari` ist ein typischer Fall für ein `collections.defaultdict`. Oder bei normalen Dictionaries zumindest für die `setdefault()`-Methode.

Das Pickle-Format würde ich hier wahrscheinlich durch JSON ersetzen. Die Daten lassen sich damit abbilden und das Format ist einfacher und verbreiteter.

Edit: Fiel mir eben noch auf: `re.split()` ist für diesen Fall Kanonen auf Spatzen. Die `split()`-Methode auf Zeichenketten geht auch *und* wenn man die ohne Argument aufruft fällt auch noch der Grund für den überflüssigen ersten ``if``-Zweig komplett weg, weil dann keine leeren Zeichenketten enthalten sind.
Hero2
User
Beiträge: 19
Registriert: Freitag 16. April 2010, 20:13

Danke, werde es dementsprechend anpassen.

Code: Alles auswählen

alle_Datein = [Datei for Datei in listdir(".") if ".txt" == Datei[-4:] ]
ist besser. Danke.
Die namen sind wirklich ein wenig schei*e. Werde ich ändern. Das mit pass brauchte ich in der 2.7 Version, weil sonst eine Leere Zeile in einer datei im Wörterbuch eine "" erzeugt hat. In der 3.3 Version brauch man das wirklich nicht. Das mit dem Modul hab ich gar nicht gesehen .... danke auch dafür, cool, dass Python da gar nicht mekert, aber na gut, macht C ja auch nicht.
PEP 8 -- Style Guide for Python Code Danke, werde ich mir genauer ansehen. Dachte sowas gibt es nur für Java.
Ok, die Split methode ändere ich.

Danke insgesammt.

Noch 2 fragen hab ich aber noch :
1.: Warum muss ich in der Version 2.7 NICHT im binärmodus öffnen ? Es geht wirklich mit :

Code: Alles auswählen

open("picklesp.pickle","r") 
Das verstehe ich nicht so ganz.

2.: Kann man eine Pickle datei von 2.7 auch irgendwie in 3.3 einlesen ? Hab das versucht, aber es ging bei mir nicht.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo HerO2,

in python 2.7 ist das default-Pickelformat noch Protocol 0, das reiner Text ist und somit auch mit "r"
geöffnet werden kann. Bei Python3 ist default Protocol 3 und binär.
Das Entpickeln von Protocol 0 in Python3 geht normalerweise problemlos.

Hier noch was für die Mitte:

Code: Alles auswählen

...
    tet = aktuelle_datei.read().split()
    for wort in set(tet):
        Diginari.setdefault(wort,[]).append(daten)
BlackJack

@Sirius3: Jedes Pickle-Protokoll ist Binär, auch 0. Du kannst ein mit Protokoll 0 in eine Textmodus-Datei geschriebenes Pickle nicht unter anderen Betriebssystemen lesen und auch unter Windows selbst nicht wenn die Datei zum Lesen im Binärmodus geöffnet wird. Bei Protokoll 0 ist es wichtig dass '\n' auch tatsächlich als *ein* Byte geschrieben wird und nicht wie im Windows-Textmodus zwei Bytes daraus werden ('\r\n'). Das kann man dann nur unter Windows und nur wenn man die Datei falsch, also im Textmodus öffnet, wieder einlesen und sonst nirgendwo. Ergo: Pickle-Dateien *immer* im Binärmodus öffnen.

Code: Alles auswählen

from collections import defaultdict
from glob import glob


def main():
    word2filenames = defaultdict(list)
    
    for filename in glob('txt/*.txt'):
        with open(filename) as file_:
            words = set(file_.read().split())
            word2filenames.update((w, filename) for w in words)
    
    print word2filenames


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@BlackJack: Interessant. Ich dachte bisher, das der Vorteil von ASCII-Pickeln wäre gerade die Systemunabhängigkeit.
Ist das ein Designfehler oder steckt da irgendeine mir entgangene Logik dahinter?
BlackJack

@Sirius3: Ich verstehe die Frage nicht. *Alle* Pickle-Protokolle sind systemunabhängig. Und alle Pickle-Protokolle sind Binärformate. Wenn man sie entsprechend behandelt, also die Daten 1:1 schreibt und liest, sind sie zwischen Systemen austauschbar. Protokoll 0 besteht nur aus Bytewerten unter 128, das bedeutet aber nicht, dass man die Bytewerte einfach so verändern darf.
Hero2
User
Beiträge: 19
Registriert: Freitag 16. April 2010, 20:13

Danke an alle. Sieht wirklich alles ganz gut aus vom Verständnis bei mir. Echt nen schönes Forum.
Antworten