#python #pickle
#python #pickle
Вопрос:
Я пытаюсь загрузить обработанный словарь, но продолжаю получать ошибку атрибута, подобную этой
TypeError: a bytes-like object is required, not '_io.BufferedReader'
Ниже приведен код для чтения и записи объекта pickle. Я загружаю обработанный объект на рабочую станцию Linux с помощью python 2.7.12. Данные передаются на Mac с помощью python 3.6.4, где выполняется readTrueData(), что приводит к вышеупомянутой ошибке.
def readTrueData(name):
fName = str('trueData/' name '.pkl')
f = open(fName,'rb')
# print(f)
# print(type(f))
pC = pickle.loads(f)
return pC
def storeTrueData(atomicConfigs, name):
import quippy
storeDic = {}
#rangeKeys = len(atomicConfigs)
#print(rangeKeys)
qTrain = quippy.AtomsList(atomicConfigs)
print(len(qTrain))
rangeKeys = len(qTrain)
print(rangeKeys)
for i in range(rangeKeys):
#configConsidered = atomicConfigs[i]
trueForce = np.array(qTrain[i].force).T
storeDic[i] = trueForce
f = open("trueData/" name ".pkl", "wb")
pickle.dump(storeDic, f)
f.close()
return None
Обновить
Работая над предложениями, упомянутыми в комментариях, я изменил свой код, как показано ниже a.) pC = pickle.load(f)
b.) pC = pickle.loads(f.read())
В обоих случаях я получил следующую ошибку
UnicodeDecodeError: 'ascii' codec can't decode byte 0x87 in position 1: ordinal not in range(128)
Ответ №1:
Ваша первая проблема вызвана несоответствием между типом аргумента и выбранным load*
методом; loads
ожидает bytes
объекты, load
ожидает сам объект file. Передача объекта file в loads
является причиной вашей ошибки.
Ваша другая проблема связана с проблемой совместимости между версиями с numpy
и datetime
типами; Python 2 загружает str
файлы без указанной кодировки, но Python 3 должен удалить их с известной кодировкой (или 'bytes'
, чтобы получить raw bytes
, а не str
raw). Для numpy
и datetime
типов, вам необходимо передать encoding='latin-1'
:
Необязательными аргументами ключевого слова являются fix_imports, encoding и errors, которые используются для управления поддержкой совместимости для потока pickle, генерируемого Python 2. Если значение fix_imports равно true, pickle попытается сопоставить старые имена Python 2 с новыми именами, используемыми в Python 3. Кодировка и ошибки подсказывают pickle, как декодировать 8-разрядные экземпляры строк, обработанные Python 2; по умолчанию они имеют значения ‘ASCII’ и ‘strict’ соответственно. Кодировкой может быть ‘bytes’, чтобы считывать эти 8-битные экземпляры строк как объекты bytes. Для удаления массивов NumPy и экземпляров datetime, date и time, выбранных Python 2, требуется использование encoding=’latin1′.
В любом случае, исправление заключается в изменении:
def readTrueData(name):
fName = str('trueData/' name '.pkl')
f = open(fName,'rb')
# print(f)
# print(type(f))
pC = pickle.loads(f)
return pC
Для:
def readTrueData(name):
fName = str('trueData/' name '.pkl')
with open(fName, 'rb') as f: # with statement avoids file leak
# Match load with file object, and provide encoding for Py2 str
return pickle.load(f, encoding='latin-1')
По соображениям корректности и производительности я бы также рекомендовал изменить pickle.dump(storeDic, f)
на pickle.dump(storeDic, f, protocol=2)
на компьютере Python 2, чтобы поток генерировался с использованием более современного протокола pickle, который, помимо прочего, может эффективно обрабатывать numpy
массивы. Протокол 0, используемый по умолчанию в Python 2, не может использовать верхний бит каждого байта (он совместим с ASCII), что означает, что необработанные двоичные данные сильно раздуваются в протоколе 0, требуя тонны изменения битов, тогда как протокол 2 может выгружать их в необработанном виде. Протокол 2 также является единственным протоколом Py2, который эффективно обрабатывает классы нового стиля, и единственным, который вообще может правильно обрабатывать определенные типы экземпляров (материал, использующий __slots__
/ __new__
и тому подобное).
Я бы также рекомендовал, чтобы скрипт начинался с:
try:
import cPickle as pickle
except ImportError:
import pickle
как и в Python 2, pickle
реализован на чистом Python, работает медленно и не позволяет использовать некоторые из более эффективных кодов pickle. На Python 3 cPickle
отсутствует, но pickle
автоматически ускоряется. В сочетании с этим и использованием протокола 2 обработка на компьютере Python 2 должна выполняться намного быстрее и давать гораздо меньшие параметры обработки.
Ответ №2:
pC = pickle.loads(f.read())
это то, что вы ищете, но вы действительно должны использовать with
контекст:
with open(fName, 'rb') as f:
pC = pickle.loads(f.read())
Это гарантирует, что ваш файл будет закрыт должным образом, особенно потому, что в вашем коде нет f.close()
в функции.
Комментарии:
1. отредактировал мой код в соответствии с вашим предложением. получена следующая ошибка UnicodeDecodeError: кодек ‘ascii’ не может декодировать байт 0x87 в позиции 1: порядковый номер отсутствует в диапазоне (128)
2. Более эффективно просто do
pC = pickle.load(f)
, что позволяет избежать большого временного буфера (loads
реализуется путем помещения аргумента вio.BytesIO
и последующего использованияload
в любом случае, поэтому использованиеload
напрямую снижает требования к памяти и процессору).3. @user1443613: Вы уверены , что соответствовали их коду? Эта ошибка похожа на ту, которую вы получили бы при открытии двоичного файла для чтения в текстовом режиме (
'r'
/'rt'
), когда файлы pickle всегда должны открываться в двоичном режиме ('rb'
). Альтернативой является то, что вы извлекаете данные из Python 2, и вам нужно передатьencoding
аргумент вload
/loads
, чтобы указать, какой должна быть предполагаемая кодировка Python 2str
(если вы понятия не имеете,latin-1
это приемлемое значение по умолчанию и, согласно документам, необходимо для извлеченияnumpy
иdatetime
типов, выбранных на Python 2).4. Спасибо… проблемы были вызваны кодировкой. Ввод encoding = ‘latin-1’ решил проблему
Ответ №3:
Вы должны использовать pickle.load(...)
для чтения, если используете open
таким образом.
Комментарии:
1. Речь идет не о сопоставлении
load
сdump
иloads
сdumps
. Речь идет о сопоставленииload
/dump
с открытыми файлами иloads/
дампах с использованиемbytes
объектов в памяти.2. изменен код на pickle. загрузить(f). Получена следующая ошибка UnicodeDecodeError: кодек ‘ascii’ не может декодировать байт 0x87 в позиции 1: порядковый номер отсутствует в диапазоне (128)