Как загрузить объект в pickle?

#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 2 str (если вы понятия не имеете, latin-1 это приемлемое значение по умолчанию и, согласно документам, необходимо для извлечения numpy и datetime типов, выбранных на Python 2).

4. Спасибо… проблемы были вызваны кодировкой. Ввод encoding = ‘latin-1’ решил проблему

Ответ №3:

Вы должны использовать pickle.load(...) для чтения, если используете open таким образом.

Источник: https://docs.python.org/3/library/pickle.html

Комментарии:

1. Речь идет не о сопоставлении load с dump и loads с dumps . Речь идет о сопоставлении load / dump с открытыми файлами и loads/ дампах с использованием bytes объектов в памяти.

2. изменен код на pickle. загрузить(f). Получена следующая ошибка UnicodeDecodeError: кодек ‘ascii’ не может декодировать байт 0x87 в позиции 1: порядковый номер отсутствует в диапазоне (128)