Sauvegarde et chargement d'objets et utilisation de pickle

114

J'essaye de sauvegarder et de charger des objets en utilisant le picklemodule.
Je déclare d'abord mes objets:

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

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

Après cela, j'ouvre un fichier appelé 'Fruits.obj' (auparavant, j'ai créé un nouveau fichier .txt et j'ai renommé 'Fruits.obj'):

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

Après cela, je ferme ma session et j'en ai commencé une nouvelle et j'ai mis la suivante (en essayant d'accéder à l'objet qu'elle supposait être sauvé):

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

Mais j'ai ce 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

Je ne sais pas quoi faire car je ne comprends pas ce message. Quelqu'un sait-il comment je peux charger mon objet «banane»? Je vous remercie!

EDIT: Comme certains d'entre vous l'ont suggéré, j'ai mis:

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

Il n'y a pas eu de problème, mais le prochain que j'ai mis était:

>>> object_file = pickle.load(file)

Et j'ai une erreur:

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
la source

Réponses:

74

Quant à votre deuxième problème:

 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

Après avoir lu le contenu du fichier, le pointeur de fichier sera à la fin du fichier - il n'y aura plus de données à lire. Vous devez rembobiner le fichier pour qu'il soit à nouveau lu depuis le début:

file.seek(0)

Cependant, ce que vous voulez généralement faire, c'est utiliser un gestionnaire de contexte pour ouvrir le fichier et en lire les données. De cette façon, le fichier sera automatiquement fermé une fois l'exécution du bloc terminée, ce qui vous aidera également à organiser vos opérations sur les fichiers en morceaux significatifs.

Enfin, cPickle est une implémentation plus rapide du module pickle en C. Donc:

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}
Jim Brissom
la source
De quel type de structure de données s'agit-il 'd = {"a": 1, "b": 2}'?
Peterstone
1
@Peterstone: {"a": 1, "b": 2}crée un dictionnaire avec les clés "a"et "b"dedans. C'est ce qu'on appelle une expression d'affichage de dictionnaire dans la documentation en ligne. C'est juste l'une des différentes manières dont un objet de type dict, qui est l'un des nombreux types de données intégrés standard disponibles en Python, peut être construit.
martineau
2
Pourquoi la lettre «r» suit-elle le nom du fichier? Je ne vois pas cela dans les documents. En outre, il est difficile d'utiliser une variable pour le nom de fichier.
SherylHohman
7
En regardant cette réponse aujourd'hui et en remarquant qu'elle ne s'applique qu'à Python 2.x. Dans Python 3.x, il faut utiliser directement picklequi importera cpickleautomatiquement si cela peut. docs.python.org/3.1/whatsnew/3.0.html#library-changes
Eskapp
41

Ce qui suit fonctionne pour moi:

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
la source
Cela fonctionne pour moi, mais ce que je recherche, c'est de fermer une session, d'en ouvrir une nouvelle et de charger ce que j'ai sauvegardé dans une session précédente. Je ferme la session après avoir mis la ligne "filehandler.close ()" et j'en ouvre une nouvelle et je mets le reste de votre code, puis après avoir mis "object_file = pickle.load (file)" j'obtiens cette erreur: Traceback ( dernier appel): Fichier "<pyshell # 5>", ligne 1, dans <module> object_file = pickle.load (fichier) Fichier "C: \ Python31 \ lib \ pickle.py", ligne 1365, en encodage de chargement = encoding, errors = errors) .load () AttributeError: l'objet 'module' n'a pas d'attribut 'Fruits'
Peterstone
3
@Peterstone: Dans la deuxième session, vous aurez besoin d'une définition de class Fruitsdéfini afin de pickle.load()pouvoir reconstituer l'objet à partir des données qui ont été enregistrées dans le fichier binaire. La meilleure pratique pour ce genre de chose est de mettre la class Fruitsdéfinition dans un fichier .py séparé (ce qui en fait un module personnalisé), puis importce module ou ses éléments chaque fois que nécessaire (c'est-à-dire les deux sessions). Par exemple, si vous le mettez dans un fichier nommé, MyDataDefs.pyvous pouvez écrire from MyDataDefs import Fruits. Faites-moi savoir si cela n'est pas clair et je mettrai à jour ma réponse en conséquence.
martineau
En fait, PEP 8 recommande d'utiliser tous les caractères minuscules pour les noms de module, donc l'exemple à la fin de mon dernier commentaire aurait dû être dans un fichier nommé my_data_defs.pyusing from my_data_defs import Fruits.
martineau
24

Vous oubliez de le lire aussi en binaire.

Dans votre partie d'écriture, vous avez:

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

Dans la partie lue, vous avez:

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

Alors remplacez-le par:

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

Et cela fonctionnera :)


Quant à votre deuxième erreur, elle est probablement due à une mauvaise fermeture / synchronisation du fichier.

Essayez ce morceau de code pour écrire:

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

Et ceci (inchangé) à lire:

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

Une version plus soignée utiliserait la withdéclaration.

Pour écrire:

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

À lire:

>>> import pickle
>>> with open('Fruits.obj', 'rb') as fp:
>>>     banana = pickle.load(fp)
Wolph
la source
1
J'utilise votre version qui utilise l'instruction with et j'obtiens ce message: Traceback (dernier appel le plus récent): File "<pyshell # 20>", ligne 1, dans <module> print (banana.color) AttributeError: 'Fruits' l'objet n'a pas d'attribut 'couleur'
Peterstone
17

Toujours ouvert en mode binaire, dans ce cas

file = open("Fruits.obj",'rb')
ismail
la source
6

Vous n'avez pas ouvert le fichier en mode binaire.

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

Devrait marcher.

Pour votre deuxième erreur, le fichier est très probablement vide, ce qui signifie que vous l'avez vidé par inadvertance ou que vous avez utilisé le mauvais nom de fichier ou quelque chose.

(Cela suppose que vous avez vraiment fermé votre session. Sinon, c'est parce que vous n'avez pas fermé le fichier entre l'écriture et la lecture).

J'ai testé votre code et cela fonctionne.

Lennart Regebro
la source
3

Il semble que vous souhaitiez enregistrer vos instances de classe entre les sessions, et l'utilisation pickleest un moyen décent de le faire. Cependant, il existe un package appelé kleptoqui fait abstraction de l'enregistrement des objets dans une interface de dictionnaire, vous pouvez donc choisir de sélectionner les objets et de les enregistrer dans un fichier (comme indiqué ci-dessous), ou de sélectionner les objets et de les enregistrer dans une base de données, ou au lieu de use pickle use json, ou bien d'autres options. La bonne chose à propos deklepto c'est que l'abstraction vers une interface commune facilite les choses afin que vous n'ayez pas à vous souvenir des détails de bas niveau sur la façon de sauvegarder via le décapage dans un fichier, ou autrement.

Notez que cela fonctionne pour les attributs de classe ajoutés dynamiquement, ce que Pickle ne peut pas faire ...

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

Puis on recommence…

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 fonctionne sur python2 et python3.

Obtenez le code ici: https://github.com/uqfoundation

Mike McKerns
la source
1

Vous pouvez utiliser anycache pour faire le travail à votre place. En supposant que vous ayez une fonction myfuncqui crée l'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 appelle myfuncà la première fois et sélectionne le résultat dans un fichier en cachedirutilisant un identifiant unique (en fonction du nom de la fonction et des arguments) comme nom de fichier. Lors de toute exécution consécutive, l'objet décapé est chargé.

Si le cachedirest conservé entre les exécutions de python, l'objet pickled est extrait de l'exécution de python précédente.

Les arguments de la fonction sont également pris en compte. Une implémentation refactorisée fonctionne de la même manière:

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
la source