Entfernen eines Python 2-Objekts mit Python 3

129

Ich frage mich, ob es eine Möglichkeit gibt, ein in Python 2.4 eingelegtes Objekt mit Python 3.4 zu laden.

Ich habe 2to3 mit einer großen Menge von Firmen-Legacy-Code ausgeführt, um ihn auf den neuesten Stand zu bringen.

Nachdem ich dies getan habe, erhalte ich beim Ausführen der Datei den folgenden Fehler:

  File "H:\fixers - 3.4\addressfixer - 3.4\trunk\lib\address\address_generic.py"
, line 382, in read_ref_files
    d = pickle.load(open(mshelffile, 'rb'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1: ordinal
not in range(128)

Wenn Sie das in Konflikt stehende eingelegte Objekt betrachten, ist es ein dictin a dict, das Schlüssel und Werte vom Typ enthält str.

Meine Frage lautet also: Gibt es eine Möglichkeit, ein ursprünglich in Python 2.4 eingelegtes Objekt mit Python 3.4 zu laden?

NDevox
quelle
1
Hat Python 2.4 das jsonModul? Vielleicht könnten Sie ein 2.4-Skript schreiben, das das Objekt entpickt und als JSON-Objekt speichert, und dann ein 3.4-Skript schreiben, das das JSON-Objekt liest und es als 3.4-kompatibles Pickle-Objekt speichert. Dies wäre eine einmalige Operation, die Sie für alle Ihre Pickle-Dateien ausführen.
Kevin
Ich habe in ähnlicher Weise gedacht, da es sich um Diktate handelt, von denen ich glaube, dass ich sys.stdout einfach in eine Datei ändern und ausdrucken könnte, aber ich möchte sehen, ob ich sie zuerst laden kann
NDevox
Verwandte Frage, die speziell mit Datumsangaben zu tun hat: stackoverflow.com/questions/24805105/…
John Y

Antworten:

189

Sie müssen pickle.load()angeben , wie Python-Bytestring-Daten in Python 3-Zeichenfolgen konvertiert werden sollen, oder Sie können festlegen pickle, dass sie als Bytes belassen werden sollen .

Standardmäßig wird versucht, alle Zeichenfolgendaten als ASCII zu dekodieren. Diese Dekodierung schlägt fehl. Siehe die pickle.load()Dokumentation :

Optionale Schlüsselwortargumente sind fix_imports , Codierung und Fehler , mit denen die Kompatibilitätsunterstützung für den von Python 2 generierten Pickle-Stream gesteuert wird . Wenn fix_imports true ist, versucht pickle , die alten Python 2-Namen den in Python 3 verwendeten neuen Namen zuzuordnen Codierung und Fehler teilen pickle mit, wie von Python 2 ausgewählte 8-Bit-String-Instanzen decodiert werden sollen. Diese sind standardmäßig 'ASCII' bzw. 'strict'. Die Codierung kann 'Bytes' sein, um diese 8-Bit-String-Instanzen als Bytes-Objekte zu lesen.

Wenn Sie die Codierung auf einstellen, latin1können Sie die Daten direkt importieren:

with open(mshelffile, 'rb') as f:
    d = pickle.load(f, encoding='latin1') 

Sie müssen jedoch überprüfen, ob keine Ihrer Zeichenfolgen mit dem falschen Codec dekodiert wurde. Latin-1 funktioniert für jede Eingabe, da die Bytewerte 0-255 direkt den ersten 256 Unicode-Codepunkten zugeordnet werden.

Die Alternative wäre, die Daten mit zu laden encoding='bytes'und anschließend alle bytesSchlüssel und Werte zu dekodieren .

Beachten Sie, dass bis zu Python-Versionen vor 3.6.8, 3.7.2 und 3.8.0 das Aufheben der Auswahl von Python 2- datetimeObjektdaten unterbrochen ist, sofern Sie diese nicht verwenden encoding='bytes'.

Martijn Pieters
quelle
1
Wie könnte dies abwärtskompatibel mit Python 2 gemacht werden? Anscheinend ist Codierungsargument für Python 2 nicht vorhanden.
EpicAdv
2
@EpicAdv: Sie müssen diesen Code nicht mit Python 2 kompatibel machen. Diese Frage ist über das Laden von Python 2 Pickles in Python 3. Löschen Sie das encodingSchlüsselwort für Python 2.
Martijn Pieters
10
@EpicAdv: Sie können ein pickle_options-Wörterbuch erstellen, das entweder für Python 2 leer ist oder 'encoding': 'latin1'** pickle_options hat und an pickle sendet. Auf diese Weise sollte es in beiden Versionen ausgeführt werden.
Pipefish
@pipefish - Clever, aber irgendwo müssen Sie erkennen, welche Version Sie verwenden, damit Sie den Aufruf je nach Version auch einfacher ausführen können (eine mit und eine ohne zusätzliches Argument). Aber zumindest haben Sie den Kern von EpicAdvs Kommentar verstanden, den Martijns Kommentar überhaupt nicht anspricht.
John Y
2
Mir ist klar, dass der datetimeKommentar nicht der Hauptschwerpunkt dieser Antwort war, aber für zukünftige Leser möchte ich darauf hinweisen, dass selbst die "festen" Versionen von Python 3 noch die encoding='latin-1'Auswahl von Python 2-Datenzeiten aufheben müssen. Wenn Ihre eingelegten Python 2-Daten sowohl Datenzeiten als auch Bytestrings enthalten, die in etwas anderem als Latin-1 codiert sind, ist es möglicherweise immer noch besser, sie zu verwenden encoding='bytes'.
John Y
15

Die Verwendung encoding='latin1'verursacht einige Probleme, wenn Ihr Objekt numpy Arrays enthält.

Verwenden encoding='bytes'wird besser sein.

In dieser Antwort finden Sie eine vollständige Erklärung der Verwendungencoding='bytes'

Sreeragh AR
quelle
Welche Probleme? Worauf sollte ich achten? using bytesmacht Strings zu Bytes (), daher bevorzuge ich es, latin1wenn möglich, aber mir ist nicht klar, wo das Problem liegt.
Gulzar
2
@ sreeragh-ar: Können Sie ein Beispiel für die Probleme geben, auf die Sie gestoßen sind? Ich habe eine zweidimensionale numpy.ndarray(numpy 1.14) in Python 2.7 mit eingelegt cPickle.dumps(), und das Entfernen in Python 3 mit pickle.loads(..., encoding='latin1')funktioniert gut.
DJVG
@djvg Ich hatte Probleme, als ich Bilder als Bildzeichenfolge auswählen und entfernen musste. Den Code finden Sie hier. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
Sreeragh AR
@ Gulzar Bitte beachten Sie die obige Übersicht für das Problem. Die Bilder wurden nach dem Abheben beschädigt.
Sreeragh AR