201

I´m trying to save and load objects using pickle module.
First I declare my objects:

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

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

After that I open a file called 'Fruits.obj'(previously I created a new .txt file and I renamed 'Fruits.obj'):

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

After do this I close my session and I began a new one and I put the next (trying to access to the object that it supposed to be saved):

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

But I have this message:

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

I don´t know what to do because I don´t understand this message. Does anyone know How I can load my object 'banana'? Thank you!

EDIT: As some of you have sugested I put:

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

There were no problem, but the next I put was:

>>> object_file = pickle.load(file)

And I have error:

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
martineau
  • 119,623
  • 25
  • 170
  • 301
Peterstone
  • 7,119
  • 14
  • 41
  • 49
  • Related: [_Saving an Object (Data persistence in Python)_](http://stackoverflow.com/questions/4529815/saving-an-object-data-persistence-in-python) – martineau Jun 08 '16 at 14:29
  • Related: [How can I use pickle to save a dict?](https://stackoverflow.com/a/33245595/562769) – Martin Thoma Aug 03 '17 at 20:23

7 Answers7

126

As for your second problem:

 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

After you have read the contents of the file, the file pointer will be at the end of the file - there will be no further data to read. You have to rewind the file so that it will be read from the beginning again:

file.seek(0)

What you usually want to do though, is to use a context manager to open the file and read data from it. This way, the file will be automatically closed after the block finishes executing, which will also help you organize your file operations into meaningful chunks.


Historical note: cPickle is a faster implementation of the pickle module in C that will be used automatically in python 3.x. But in python 2.x it cPickle required explicit calls:

In [1]: import _pickle as 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}
mirekphd
  • 4,799
  • 3
  • 38
  • 59
Jim Brissom
  • 31,821
  • 4
  • 39
  • 33
  • What kind of data structure is this 'd = {"a": 1, "b": 2}' ? – Peterstone Dec 25 '10 at 15:56
  • 1
    @Peterstone: `{"a": 1, "b": 2}` creates a dictionary with the keys `"a"` and `"b"` in it. This is called a [dictionary display expression](http://docs.python.org/py3k/reference/expressions.html#dictionary-displays) in the online documentation. It's just one of the several different ways an object of type [`dict`](http://docs.python.org/py3k/library/stdtypes.html#dict), which is one of several standard built-in datatypes available in Python, can be constructed. – martineau Dec 25 '10 at 21:52
  • 2
    Why does the letter 'r' proceed the filename? I don't see that in the docs. Also, it makes it difficult to use a variable for the filename. – SherylHohman Apr 27 '17 at 05:09
  • 12
    Looking at this answer today and noticing it only applies to Python 2.x. In Python 3.x, one should directly use `pickle` that will import `cpickle` automatically if it cans. https://docs.python.org/3.1/whatsnew/3.0.html#library-changes – Eskapp Jul 25 '17 at 14:16
  • People using the `pickle` module should keep in mind that [it is not secure](https://docs.python.org/3/library/pickle.html) and should only be used to unpickle data from trusted sources as there is the possibility for arbitrary code execution during the unpickling process. If you are producing pickles, consider signing data with [hmac](https://docs.python.org/3/library/hmac.html#module-hmac) to ensure data has not been tampered with, or using alternative forms of serialisation like [JSON](https://docs.python.org/3/library/pickle.html#comparison-with-json). – Kyle F Hartzenberg Apr 12 '23 at 04:26
70

The following works for me:

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
martineau
  • 119,623
  • 25
  • 170
  • 301
  • This works me, but what I pursuit is to close a session, open a new one and load what I save in a past session. I close the session after putting the line" filehandler.close()" and I open a new one and I put the rest of your code, then after putting "object_file = pickle.load(file)" I get this error:Traceback (most recent call last): File "", line 1, in object_file = pickle.load(file) File "C:\Python31\lib\pickle.py", line 1365, in load encoding=encoding, errors=errors).load() AttributeError: 'module' object has no attribute 'Fruits' – Peterstone Jan 01 '11 at 11:39
  • 4
    @Peterstone: In the second session you'll need to have a definition of `class Fruits` defined so that `pickle.load()` can reconstitute the object from the data that was saved in the binary file. The best practice for this sort of thing is to put the `class Fruits` definition in a separate .py file (making it a custom module) and then `import` that module or items from it whenever needed (i.e. both sessions). For example if you put it in a file named `MyDataDefs.py` then you could write `from MyDataDefs import Fruits`. Let me know if this is unclear and I will update my answer accordingly. – martineau Jan 01 '11 at 20:07
  • Actually PEP 8 recommends using [all lowercase characters](https://www.python.org/dev/peps/pep-0008/#package-and-module-names) for module names, so the example at the end of my last comment should have been in a file named `my_data_defs.py` using `from my_data_defs import Fruits`. – martineau Jan 13 '15 at 09:28
  • What do you think about using file handlers? – Martin Thoma Feb 05 '22 at 08:49
  • @Martin Thoma: Your question is too vague — what exactly do you mean? – martineau Apr 02 '22 at 15:31
  • 1
    Sorry, I meant using context handlers (`with open() as f: ...`). So you don't need to memorize the `file.close()`. – Martin Thoma Apr 02 '22 at 18:06
  • @Martin Thoma: Was only mimicking the OP's code — personally always use `with`. – martineau Apr 02 '22 at 20:54
52

You're forgetting to read it as binary too.

In your write part you have:

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

In the read part you have:

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

So replace it with:

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

And it will work :)


As for your second error, it is most likely cause by not closing/syncing the file properly.

Try this bit of code to write:

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

And this (unchanged) to read:

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

A neater version would be using the with statement.

For writing:

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

For reading:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)
Wolph
  • 78,177
  • 11
  • 137
  • 148
  • 1
    I use your version that use the with statement and I obtain this message: Traceback (most recent call last): File "", line 1, in print(banana.color) AttributeError: 'Fruits' object has no attribute 'color' – Peterstone Dec 25 '10 at 17:26
17

Always open in binary mode, in this case

file = open("Fruits.obj",'rb')
ismail
  • 46,010
  • 9
  • 86
  • 95
5

You can use anycache to do the job for you. Assuming you have a function myfunc which creates the instance:

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 calls myfunc at the first time and pickles the result to a file in cachedir using an unique identifier (depending on the the function name and the arguments) as filename. On any consecutive run, the pickled object is loaded.

If the cachedir is preserved between python runs, the pickled object is taken from the previous python run.

The function arguments are also taken into account. A refactored implementation works likewise:

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
  • 651
  • 8
  • 4
4

You didn't open the file in binary mode.

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

Should work.

For your second error, the file is most likely empty, which mean you inadvertently emptied it or used the wrong filename or something.

(This is assuming you really did close your session. If not, then it's because you didn't close the file between the write and the read).

I tested your code, and it works.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
3

It seems you want to save your class instances across sessions, and using pickle is a decent way to do this. However, there's a package called klepto that abstracts the saving of objects to a dictionary interface, so you can choose to pickle objects and save them to a file (as shown below), or pickle the objects and save them to a database, or instead of use pickle use json, or many other options. The nice thing about klepto is that by abstracting to a common interface, it makes it easy so you don't have to remember the low-level details of how to save via pickling to a file, or otherwise.

Note that It works for dynamically added class attributes, which pickle cannot do...

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()
>>> 

Then we restart…

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 works on python2 and python3.

Get the code here: https://github.com/uqfoundation

Mike McKerns
  • 33,715
  • 8
  • 119
  • 139