Rozpakowywanie obiektu Python 2 za pomocą Pythona 3

135

Zastanawiam się, czy istnieje sposób na załadowanie obiektu, który został wytrawiony w Pythonie 2.4 za pomocą Pythona 3.4.

Używałem 2to3 na dużej ilości starszego kodu firmy, aby go zaktualizować.

Po wykonaniu tej czynności podczas uruchamiania pliku pojawia się następujący błąd:

  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)

Patrząc na marynowany obiekt będący w sprzeczności, jest to a dictin a dict, zawierający klucze i wartości typu str.

Moje pytanie brzmi: czy istnieje sposób na załadowanie obiektu, pierwotnie wytrawionego w Pythonie 2.4, za pomocą Pythona 3.4?

NDevox
źródło
1
Czy Python 2.4 ma jsonmoduł? Być może mógłbyś napisać skrypt 2.4, który odblokowuje obiekt i zapisuje go jako obiekt json, a następnie napisać skrypt 3.4, który odczytuje obiekt json i zapisuje go jako obiekt pickle zgodny z 3.4. Byłaby to jednorazowa operacja, którą wykonujesz na wszystkich swoich plikach marynat.
Kevin
Myślałem w podobny sposób, biorąc pod uwagę, że są to nakazy. Myślę, że mógłbym po prostu zmienić sys.stdout na plik i wydrukować je, ale chcę zobaczyć, czy mogę je najpierw załadować
NDevox
Powiązane pytanie odnoszące się konkretnie do czasów dat: stackoverflow.com/questions/24805105/…
John Y

Odpowiedzi:

196

Będziesz musiał powiedzieć, pickle.load()jak przekonwertować dane testowe Pythona na ciągi Python 3 lub możesz picklezostawić je jako bajty.

Domyślnie próbuje się zdekodować wszystkie dane łańcuchowe jako ASCII, a dekodowanie kończy się niepowodzeniem. Zobacz pickle.load()dokumentację :

Opcjonalne argumenty słów kluczowych to fix_imports , kodowanie i błędy , które są używane do kontrolowania obsługi zgodności dla strumienia pikle generowanego przez Python 2. Jeśli fix_imports ma wartość true, pickle spróbuje zmapować stare nazwy Python 2 na nowe nazwy używane w Pythonie 3. kodowanie i błędy mówią pikle, jak dekodować 8-bitowe instancje ciągów marynowane przez Python 2; te domyślne to odpowiednio „ASCII” i „ścisłe”. Kodowanie może być „” bajtów do odczytu tych wystąpień 8-bitowy ciąg jako bajty obiektów.

Ustawienie kodowania na latin1umożliwia bezpośredni import danych:

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

ale musisz sprawdzić, czy żaden z twoich ciągów nie jest dekodowany przy użyciu niewłaściwego kodeka; Latin-1 działa dla każdego wejścia, ponieważ bezpośrednio mapuje wartości bajtów 0-255 na pierwsze 256 punktów kodowych Unicode.

Alternatywą byłoby załadowanie danych i późniejsze encoding='bytes'zdekodowanie wszystkich byteskluczy i wartości.

Zauważ, że do wersji Pythona wcześniejszych niż 3.6.8, 3.7.2 i 3.8.0 usuwanie datetimedanych obiektu Python 2 jest przerywane, chyba że używasz encoding='bytes'.

Martijn Pieters
źródło
1
Jak można to uczynić wstecznie kompatybilnym z Pythonem 2? Najwyraźniej argument kodowania nie występuje w Pythonie 2.
EpicAdv
2
@EpicAdv: nie musisz czynić tego kodu kompatybilnym z Pythonem 2; to pytanie dotyczy tego, jak załadować pikle z Pythona 2 do Pythona 3. encodingCałkowicie upuść słowo kluczowe dla Pythona 2.
Martijn Pieters
10
@EpicAdv: Możesz utworzyć słownik pickle_options, który jest albo pusty dla Pythona 2, albo ma 'encoding': 'latin1'i wysyła ** pickle_options do pickle. W ten sposób powinien działać w obu wersjach.
pipefish
@pipefish - Sprytne, ale gdzieś musisz wykryć, której wersji używasz, więc możesz też prościej po prostu wykonać połączenie inaczej (jedno z dodatkowym argumentem, a drugie bez dodatkowego argumentu) w zależności od wersji. Ale przynajmniej rozumiesz sedno komentarza EpicAdv, którego komentarz Martijna w ogóle nie dotyczy.
John Y
2
Zdaję sobie sprawę, że datetimekomentarz nie był głównym motywem tej odpowiedzi, ale dla przyszłych czytelników chciałbym zwrócić uwagę, że nawet „naprawione” wersje Pythona 3 nadal wymagają encoding='latin-1'odblokowania czasów danych w Pythonie 2. Jeśli twoje marynowane dane Pythona 2 zawierają zarówno czas danych, jak i bajty zakodowane w czymś innym niż Latin-1, to mimo wszystko może być lepiej encoding='bytes'.
John Y
16

Używanie encoding='latin1'powoduje pewne problemy, gdy twój obiekt zawiera tablice numpy.

Korzystanie encoding='bytes'będzie lepsze.

Zapoznaj się z tą odpowiedzią, aby uzyskać pełne wyjaśnienie użyciaencoding='bytes'

Sreeragh AR
źródło
Jakie problemy? Na co mam uważać? użycie byteszamienia łańcuchy w bajty (), więc wolę, latin1jeśli to możliwe, ale nie jest dla mnie jasne, na czym polega problem.
Gulzar
2
@ sreeragh-ar: Czy możesz podać przykład napotkanych problemów? Mam dwuwymiarowy numpy.ndarray(numpy 1.14) marynowany w Pythonie 2.7 przy użyciu cPickle.dumps()i rozpakowywanie w Pythonie 3 z pickle.loads(..., encoding='latin1')działa dobrze.
djvg
@djvg Napotkałem problemy, gdy musiałem wytrawić obrazy jako ciąg obrazów i je rozpakować. Kod można znaleźć tutaj. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
Sreeragh AR
@Gulzar Proszę zapoznać się z powyższym streszczeniem problemu. Obrazy ulegały uszkodzeniu po wytrawieniu.
Sreeragh AR