Disimballare un oggetto python 2 con python 3


129

Mi chiedo se c'è un modo per caricare un oggetto che è stato decapato in Python 2.4, con Python 3.4.

Ho eseguito 2to3 su una grande quantità di codice legacy dell'azienda per aggiornarlo.

Fatto ciò, quando eseguo il file ottengo il seguente errore:

  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)

Guardando l'oggetto in salamoia in contesa, è un dictin a dict, contenente chiavi e valori di tipo str.

Quindi la mia domanda è: c'è un modo per caricare un oggetto, originariamente decapato in Python 2.4, con Python 3.4?


1
Python 2.4 ha il jsonmodulo? Forse potresti scrivere uno script 2.4 che scompone l'oggetto e lo salva come oggetto json, quindi scrivi uno script 3.4 che legge l'oggetto json e lo salva come oggetto pickle 3.4 compatibile. Questa sarebbe un'operazione una tantum che esegui su tutti i tuoi file pickle.
Kevin,

Stavo pensando in modo simile, considerando che si tratta di dicts, penso che potrei semplicemente cambiare sys.stdout in un file e stamparli, ma voglio vedere se riesco a caricarli prima
NDevox,

Domanda correlata che riguarda in particolare i tempi: stackoverflow.com/questions/24805105/…
John Y

Risposte:


189

Dovrai dire pickle.load()come convertire Python restringendo i dati alle stringhe di Python 3, oppure puoi dire pickledi lasciarli come byte.

L'impostazione predefinita è provare e decodificare tutti i dati di stringa come ASCII e tale decodifica non riesce. Vedi la pickle.load()documentazione :

Gli argomenti di parole chiave opzionali sono fix_imports , codifica ed errori , che vengono utilizzati per controllare il supporto di compatibilità per il flusso di pickle generato da Python 2. Se fix_imports è true, pickle proverà a mappare i vecchi nomi Python 2 ai nuovi nomi utilizzati in Python 3. la codifica e gli errori indicano a pickle come decodificare istanze di stringa a 8 bit decapate da Python 2; questi sono impostati rispettivamente su "ASCII" e "rigoroso". La codifica può essere "byte" per leggere queste istanze di stringa a 8 bit come oggetti byte.

L'impostazione della codifica su latin1consente di importare direttamente i dati:

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

ma dovrai verificare che nessuna delle tue stringhe sia decodificata usando il codec sbagliato; Latin-1 funziona per qualsiasi input in quanto mappa direttamente i valori di byte 0-255 ai primi 256 codici Unicode.

L'alternativa sarebbe caricare i dati encoding='bytes'e decodificare tutte le byteschiavi e i valori in seguito.

Si noti che fino alle versioni Python precedenti alla 3.6.8, 3.7.2 e 3.8.0, la decompressione dei datetimedati degli oggetti Python 2 viene interrotta a meno che non venga utilizzata encoding='bytes'.


1
Come è possibile renderlo retrocompatibile con Python 2? Apparentemente, l'argomento di codifica non è presente per Python 2.
EpicAdv

2
@EpicAdv: non è necessario rendere questo codice compatibile con Python 2; questa domanda è su come caricare i sottaceti Python 2 in Python 3. Rilasciare completamente la encodingparola chiave per Python 2.
Martijn Pieters

10
@EpicAdv: puoi creare un dizionario pickle_options che è vuoto per Python 2 o ha 'encoding': 'latin1'e inviare ** pickle_options a pickle. In questo modo dovrebbe funzionare in entrambe le versioni.
pipefish,

@pipefish - Intelligente, ma da qualche parte devi rilevare quale versione stai usando, quindi potresti anche fare più semplicemente la chiamata in modo diverso (uno con e uno senza l'argomento aggiuntivo) a seconda della versione. Ma almeno hai capito il commento di EpicAdv, che il commento di Martijn non affronta affatto.
John Y

2
Mi rendo conto che il datetimecommento non è stato il fulcro principale di questa risposta, ma per i futuri lettori, vorrei sottolineare che anche le versioni "fisse" di Python 3 richiedono ancora encoding='latin-1'di decomprimere i tempi di trasmissione di Python 2. Se i tuoi dati Python 2 in decapaggio includono sia i periodi di dati che i bytestring codificati in qualcosa di diverso da Latin-1, allora potresti essere ancora meglio usare encoding='bytes'dopo tutto.
John Y,

15

L'uso encoding='latin1'causa alcuni problemi quando l'oggetto contiene matrici intorpidite al suo interno.

L'uso encoding='bytes'sarà migliore.

Vedere questa risposta per una spiegazione completa dell'usoencoding='bytes'


Quali problemi? Di cosa dovrei stare attento? usando bytestrasforma stringhe in byte (), quindi preferisco latin1se possibile, ma non mi è chiaro quale sia il problema.
Gulzar,

2
@ sreeragh-ar: potresti fare un esempio dei problemi che hai riscontrato? Ho un bidimensionale numpy.ndarray(intorpidito 1.14) in decapaggio in Python 2.7 usando cPickle.dumps()e scompattando in Python 3 con le pickle.loads(..., encoding='latin1')opere bene.
DJGG

@djvg Ho riscontrato dei problemi quando ho dovuto mettere in ordine le immagini come stringa di immagini e scartarle. Il codice può essere trovato qui. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
Sreeragh AR

@Gulzar Si prega di consultare l'essenziale sopra per il problema. Le immagini si stavano corrompendo dopo averle scartate.
Sreeragh AR,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.