Распаковка объекта Python 2 с помощью Python 3

129

Мне интересно, есть ли способ загрузить объект, который был обработан в Python 2.4, с помощью Python 3.4.

Я использовал 2to3 для большого количества устаревшего кода компании, чтобы обновить его.

Сделав это, при запуске файла я получаю следующую ошибку:

  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)

Глядя на замаринованный объект в состязании, он находится dictв a dict, содержащем ключи и значения типа str.

Итак, мой вопрос: есть ли способ загрузить объект, изначально маринованный в python 2.4, с помощью python 3.4?

NDevox
источник
1
Есть ли в Python 2.4 jsonмодуль? Возможно, вы могли бы написать сценарий 2.4, который распаковывает объект и сохраняет его как объект json, а затем написать сценарий 3.4, который считывает объект json и сохраняет его как совместимый с 3.4 объект pickle. Это будет одноразовая операция, которую вы выполняете для всех файлов рассола.
Кевин
Я думал в том же духе, учитывая, что это диктовки, которые, как мне кажется, я мог бы просто изменить sys.stdout в файл и распечатать их, но я хочу посмотреть, смогу ли я сначала загрузить их
NDevox
Связанный вопрос, конкретно связанный с датами: stackoverflow.com/questions/24805105/…
John Y

Ответы:

189

Вам нужно будет рассказать, pickle.load()как преобразовать данные строки байтов Python в строки Python 3, или вы можете pickleоставить их в виде байтов.

По умолчанию выполняется попытка декодирования всех строковых данных как ASCII, и это декодирование не удается. См. pickle.load()Документацию :

Необязательными аргументами ключевого слова являются fix_imports , кодировка и ошибки , которые используются для управления поддержкой совместимости для потока pickle, созданного Python 2. Если fix_imports истинно, pickle попытается сопоставить старые имена Python 2 с новыми именами, используемыми в Python 3. кодирование и ошибки сообщают pickle, как декодировать экземпляры 8-битных строк, обработанные Python 2; по умолчанию они имеют значения «ASCII» и «строгий» соответственно. Кодирование может быть «байт» , чтобы прочитать эти 8-битовую строку экземпляров , как байты объектов.

Установка кодировки latin1позволяет напрямую импортировать данные:

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

но вам нужно убедиться, что ни одна из ваших строк не декодируется с использованием неправильного кодека; Latin-1 работает для любого ввода, поскольку он напрямую сопоставляет байтовые значения 0–255 с первыми 256 кодовыми точками Unicode.

Альтернативой может быть загрузка данных и последующее encoding='bytes'декодирование всех bytesключей и значений.

Обратите внимание, что до версий Python до 3.6.8, 3.7.2 и 3.8.0 распаковка данных datetimeобъекта Python 2 не выполняется, если вы не используете encoding='bytes'.

Мартейн Питерс
источник
1
Как сделать это обратно совместимым с Python 2? По-видимому, аргумент кодировки отсутствует для Python 2.
EpicAdv
2
@EpicAdv: вам не нужно делать этот код совместимым с Python 2; этот вопрос о том, как загрузить соленые огурцы Python 2 в Python 3. encoding
Мартейн Питерс
10
@EpicAdv: вы можете создать словарь pickle_options, который либо пуст для python 2, либо содержит 'encoding': 'latin1'** pickle_options и отправляет его в pickle. Таким образом, он должен работать в обеих версиях.
pipefish
@pipefish - умно, но где-то вам нужно определить, какую версию вы используете, поэтому вы также можете проще просто выполнить вызов по-разному (один с и один без дополнительного аргумента) в зависимости от версии. Но, по крайней мере, вы поняли суть комментария EpicAdv, к которому комментарий Мартейн вообще не относится.
John Y
2
Я понимаю, что datetimeкомментарий не был основной темой этого ответа, но для будущих читателей я хотел бы указать, что даже «фиксированные» версии Python 3 по-прежнему требуют encoding='latin-1'отмены выбора времени Python 2. Если ваши консервированные данные Python 2 включают в себя как даты, так и строки байтов, закодированные в чем-то отличном от Latin-1, вам все равно может быть лучше использовать encoding='bytes'.
John Y
15

Использование encoding='latin1'вызывает некоторые проблемы, когда ваш объект содержит в себе массивы numpy.

Использование encoding='bytes'будет лучше.

Пожалуйста, посмотрите этот ответ для полного объяснения использованияencoding='bytes'

Sreeragh AR
источник
Какие проблемы? Чего мне следует остерегаться? использование bytesпревращает строки в bytes (), поэтому я предпочитаю, latin1если возможно, но мне не ясно, в чем проблема.
Гульзар
2
@ sreeragh-ar: Не могли бы вы привести пример проблем, с которыми вы столкнулись? У меня есть двумерный numpy.ndarray(numpy 1.14), маринованный в Python 2.7 cPickle.dumps(), и распаковка в Python 3 с pickle.loads(..., encoding='latin1')отлично работает.
djvg
@djvg Я столкнулся с проблемами, когда мне приходилось собирать изображения как строку изображения и распаковывать их. Код можно найти здесь. gist.github.com/sreeragh-ar/70205db3a43badbfa69f758faa898be3
Sreeragh AR
@Gulzar Пожалуйста, прочтите суть проблемы выше. После распаковки изображения были повреждены.
Sreeragh AR