220

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
2

7 Answers 7

140

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}
Sign up to request clarification or add additional context in comments.

5 Comments

What kind of data structure is this 'd = {"a": 1, "b": 2}' ?
@Peterstone: {"a": 1, "b": 2} creates a dictionary with the keys "a" and "b" in it. This is called a dictionary display expression in the online documentation. It's just one of the several different ways an object of type dict, which is one of several standard built-in datatypes available in Python, can be constructed.
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.
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. docs.python.org/3.1/whatsnew/3.0.html#library-changes
People using the pickle module should keep in mind that it is not secure 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 to ensure data has not been tampered with, or using alternative forms of serialisation like JSON.
75

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

7 Comments

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 "<pyshell#5>", line 1, in <module> 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: 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.
Actually PEP 8 recommends using all lowercase characters 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.
What do you think about using file handlers?
Sorry, I meant using context handlers (with open() as f: ...). So you don't need to memorize the file.close().
|
53

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)

1 Comment

I use your version that use the with statement and I obtain this message: Traceback (most recent call last): File "<pyshell#20>", line 1, in <module> print(banana.color) AttributeError: 'Fruits' object has no attribute 'color'
17

Always open in binary mode, in this case

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

Comments

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

Comments

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.

Comments

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.