Сохранение и загрузка объектов и использование рассола

115

Я пытаюсь сохранять и загружать объекты с помощью pickleмодуля.
Сначала объявляю свои объекты:

>>> class Fruits:pass
...
>>> banana = Fruits()

>>> banana.color = 'yellow'
>>> banana.value = 30

После этого я открываю файл с именем Fruits.obj (ранее я создал новый файл .txt и переименовал его в Fruits.obj):

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)

После этого я закрываю свой сеанс, начинаю новый и помещаю следующий (пытаюсь получить доступ к объекту, который должен быть сохранен):

file = open("Fruits.obj",'r')
object_file = pickle.load(file)

Но у меня есть такое сообщение:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
ValueError: read() from the underlying stream did notreturn bytes

Я не знаю, что делать, потому что не понимаю этого сообщения. Кто-нибудь знает, как я могу загрузить свой объект "банан"? Спасибо!

РЕДАКТИРОВАТЬ: Как некоторые из вас предположили, я поставил:

>>> import pickle
>>> file = open("Fruits.obj",'rb')

Проблем не было, но я поставил следующее:

>>> object_file = pickle.load(file)

И у меня ошибка:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
EOFError
Peterstone
источник

Ответы:

75

Что касается вашей второй проблемы:

 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "C:\Python31\lib\pickle.py", line
 1365, in load encoding=encoding,
 errors=errors).load() EOFError

После того, как вы прочитаете содержимое файла, указатель файла будет в конце файла - больше не будет данных для чтения. Вам нужно перемотать файл назад, чтобы он снова читался с начала:

file.seek(0)

Однако обычно вы хотите использовать диспетчер контекста, чтобы открыть файл и прочитать из него данные. Таким образом, файл будет автоматически закрыт после завершения выполнения блока, что также поможет вам организовать файловые операции в значимые фрагменты.

Наконец, cPickle - это более быстрая реализация модуля pickle на C. Итак:

In [1]: import cPickle

In [2]: d = {"a": 1, "b": 2}

In [4]: with open(r"someobject.pickle", "wb") as output_file:
   ...:     cPickle.dump(d, output_file)
   ...:

# pickle_file will be closed at this point, preventing your from accessing it any further

In [5]: with open(r"someobject.pickle", "rb") as input_file:
   ...:     e = cPickle.load(input_file)
   ...:

In [7]: print e
------> print(e)
{'a': 1, 'b': 2}
Джим Бриссом
источник
Что это за структура данных 'd = {"a": 1, "b": 2}'?
Peterstone
1
@Peterstone: {"a": 1, "b": 2}создает словарь с ключами "a"и "b"в нем. В онлайн-документации это называется выражением отображения словаря . Это всего лишь один из нескольких способов создания объекта типа dict, который является одним из нескольких стандартных встроенных типов данных, доступных в Python.
Мартино
2
Почему после имени файла стоит буква «r»? Я не вижу этого в документации. Кроме того, это затрудняет использование переменной для имени файла.
SherylHohman
8
Глядя на этот ответ сегодня и замечая, что он применим только к Python 2.x. В Python 3.x следует напрямую использовать pickleто, что будет импортировать cpickleавтоматически, если это возможно. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Эскапп,
41

Для меня работает следующее:

class Fruits: pass

banana = Fruits()

banana.color = 'yellow'
banana.value = 30

import pickle

filehandler = open("Fruits.obj","wb")
pickle.dump(banana,filehandler)
filehandler.close()

file = open("Fruits.obj",'rb')
object_file = pickle.load(file)
file.close()

print(object_file.color, object_file.value, sep=', ')
# yellow, 30
Мартино
источник
У меня это работает, но я стараюсь закрыть сеанс, открыть новый и загрузить то, что я сохранил в прошлом сеансе. Я закрываю сеанс после того, как помещаю строку «filehandler.close ()», и я открываю новый, и я помещаю остальную часть вашего кода, а затем, помещая «object_file = pickle.load (file)», я получаю эту ошибку: Traceback ( последний вызов последним): файл «<pyshell # 5>», строка 1, в <module> object_file = pickle.load (файл) Файл «C: \ Python31 \ lib \ pickle.py», строка 1365, в кодировке загрузки = кодировка, ошибки = ошибки) .load () AttributeError: объект 'module' не имеет атрибута 'Fruits'
Peterstone
3
@Peterstone: во втором сеансе вам нужно будет определить class Fruitsопределение, чтобы pickle.load()можно было восстановить объект из данных, которые были сохранены в двоичном файле. Лучшая практика для такого рода вещей - поместить class Fruitsопределение в отдельный файл .py (сделав его настраиваемым модулем), а затем importэтот модуль или элементы из него, когда это необходимо (т. Е. Оба сеанса). Например, если вы поместите его в файл с именем, MyDataDefs.pyвы можете написать from MyDataDefs import Fruits. Сообщите мне, если это непонятно, и я соответствующим образом обновлю свой ответ.
Мартино 01
На самом деле PEP 8 рекомендует использовать все символы нижнего регистра для имен модулей, поэтому пример в конце моего последнего комментария должен был быть в файле с именем my_data_defs.pyusing from my_data_defs import Fruits.
Мартино
24

Вы тоже забываете читать его как двоичный файл.

В вашей части записи у вас есть:

open(b"Fruits.obj","wb") # Note the wb part (Write Binary)

В читаемой части у вас есть:

file = open("Fruits.obj",'r') # Note the r part, there should be a b too

Поэтому замените его на:

file = open("Fruits.obj",'rb')

И будет работать :)


Что касается вашей второй ошибки, она, скорее всего, вызвана неправильным закрытием / синхронизацией файла.

Попробуйте написать этот фрагмент кода:

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)
>>> filehandler.close()

И это (без изменений) читать:

>>> import pickle
>>> file = open("Fruits.obj",'rb')
>>> object_file = pickle.load(file)

Более аккуратная версия будет использовать это withутверждение.

Для записи:

>>> import pickle
>>> with open('Fruits.obj', 'wb') as fp:
>>>     pickle.dump(banana, fp)

Для чтения:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)
Wolph
источник
1
Я использую вашу версию, в которой используется оператор with, и получаю следующее сообщение: Traceback (последний вызов последний): файл «<pyshell # 20>», строка 1, в <module> print (banana.color) AttributeError: 'Fruits' объект не имеет атрибута 'цвет'
Peterstone
17

Всегда открывать в двоичном режиме, в этом случае

file = open("Fruits.obj",'rb')
исмаил
источник
6

Вы не открыли файл в двоичном режиме.

open("Fruits.obj",'rb')

Должно сработать.

Для вашей второй ошибки файл, скорее всего, пуст, что означает, что вы случайно очистили его или использовали неправильное имя файла или что-то в этом роде.

(Предполагается, что вы действительно закрыли сеанс. Если нет, то это потому, что вы не закрыли файл между записью и чтением).

Я протестировал ваш код, и он работает.

Леннарт Регебро
источник
3

Кажется, вы хотите сохранять экземпляры классов между сеансами, и использование pickle- достойный способ сделать это. Однако есть пакет, называемый, kleptoкоторый абстрагирует сохранение объектов в интерфейсе словаря, поэтому вы можете выбрать объединение объектов и сохранение их в файл (как показано ниже), или объединение объектов и сохранение их в базе данных, или вместо использовать pickle use json или многие другие варианты. Хорошая вещь оklepto что абстрагирование от общего интерфейса упрощает задачу, так что вам не нужно запоминать низкоуровневые детали того, как сохранить через консервирование в файл или иным образом.

Обратите внимание, что он работает для динамически добавляемых атрибутов класса, чего не может сделать pickle ...

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from klepto.archives import file_archive 
>>> db = file_archive('fruits.txt')
>>> class Fruits: pass
... 
>>> banana = Fruits()
>>> banana.color = 'yellow'
>>> banana.value = 30
>>> 
>>> db['banana'] = banana 
>>> db.dump()
>>> 

Затем перезапускаем…

dude@hilbert>$ python
Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from klepto.archives import file_archive
>>> db = file_archive('fruits.txt')
>>> db.load()
>>> 
>>> db['banana'].color
'yellow'
>>> 

Klepto работает на python2 и python3.

Получите код здесь: https://github.com/uqfoundation

Майк МакКернс
источник
1

Вы можете использовать anycache, чтобы сделать эту работу за вас. Предположим, у вас есть функция, myfuncкоторая создает экземпляр:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc()
    banana = Fruits()
    banana.color = 'yellow'
    banana.value = 30
return banana

Anycache вызывает myfuncв первый раз и сохраняет результат в файл, cachedirиспользуя уникальный идентификатор (в зависимости от имени функции и аргументов) в качестве имени файла. При любом последовательном прогоне загружается маринованный объект.

Если cachedirсохраняется между запусками python, маринованный объект берется из предыдущего запуска python.

Также учитываются аргументы функции. Реорганизованная реализация работает аналогично:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc(color, value)
    fruit = Fruits()
    fruit.color = color
    fruit.value = value
return fruit
c0fec0de
источник