312 votes

Sauvegarde d'un objet (persistance des données)

J'ai créé un objet comme ceci :

company1.name = 'banana' 
company1.value = 40

Je voudrais sauvegarder cet objet. Comment puis-je le faire ?

1 votes

Voir exemple pour les personnes qui viennent ici pour un simple exemple d'utilisation du cornichon.

0 votes

@MartinThoma : Pourquoi préférez-vous (apparemment) cette réponse à celle qui est acceptée (du question liée ) ?

0 votes

Au moment où j'ai fait le lien, la réponse acceptée n'avait pas protocol=pickle.HIGHEST_PROTOCOL . Ma réponse donne également des alternatives au cornichon.

584voto

martineau Points 21665

Vous pourriez utiliser le pickle dans la bibliothèque standard. Voici une application élémentaire de celui-ci à votre exemple :

import pickle

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

with open('company_data.pkl', 'wb') as output:
    company1 = Company('banana', 40)
    pickle.dump(company1, output, pickle.HIGHEST_PROTOCOL)

    company2 = Company('spam', 42)
    pickle.dump(company2, output, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as input:
    company1 = pickle.load(input)
    print(company1.name)  # -> banana
    print(company1.value)  # -> 40

    company2 = pickle.load(input)
    print(company2.name) # -> spam
    print(company2.value)  # -> 42

Vous pouvez également définir votre propre utilitaire simple, comme le suivant, qui ouvre un fichier et y écrit un seul objet :

def save_object(obj, filename):
    with open(filename, 'wb') as output:  # Overwrites any existing file.
        pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')

Mise à jour

Comme il s'agit d'une réponse très populaire, j'aimerais aborder quelques sujets d'utilisation un peu plus avancés.

cPickle (o _pickle ) vs pickle

Il est presque toujours préférable d'utiliser réellement l'option cPickle plutôt que le module pickle car le premier est écrit en C et est beaucoup plus rapide. Il existe quelques différences subtiles entre les deux, mais dans la plupart des situations, elles sont équivalentes et la version C offre des performances nettement supérieures. Le passage à cette version ne pourrait pas être plus facile, il suffit de modifier le paramètre import à cette déclaration :

import cPickle as pickle

Dans Python 3, cPickle a été renommé _pickle mais cela n'est plus nécessaire puisque le système de gestion de l'information de la Commission européenne a été modifié. pickle le module le fait maintenant automatiquement - voir Quelle différence entre pickle et _pickle dans python 3 ? .

En résumé, vous pourriez utiliser quelque chose comme ce qui suit pour vous assurer que votre code sera siempre utiliser la version C lorsqu'elle est disponible à la fois dans Python 2 et 3 :

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

Formats de flux de données (protocoles)

pickle peut lire et écrire des fichiers dans plusieurs formats différents, spécifiques à Python, appelés protocoles comme décrit dans le documentation La version 0 du protocole est en ASCII et donc "lisible par l'homme". Les versions > 0 sont binaires et la version la plus élevée disponible dépend de la version de Python utilisée. La valeur par défaut dépend également de la version de Python. Dans Python 2, la valeur par défaut était Protocol version 0 mais dans Python 3.8.1, il s'agit de la version "Protocol". 4 . Dans Python 3.x, le module possédait un fichier pickle.DEFAULT_PROTOCOL ajouté à celui-ci, mais cela n'existe pas dans Python 2.

Heureusement, il existe un raccourci pour écrire pickle.HIGHEST_PROTOCOL dans chaque appel (en supposant que c'est ce que vous voulez, et c'est généralement le cas), utilisez simplement le nombre littéral -1 - similaire à la référence au dernier élément d'une séquence via un index négatif. Ainsi, au lieu d'écrire :

pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

Tu peux juste écrire :

pickle.dump(obj, output, -1)

Dans tous les cas, vous n'aurez à spécifier le protocole qu'une seule fois si vous créez un fichier Pickler pour être utilisé dans des opérations de décapage multiples :

pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

Note : Si vous êtes dans un environnement exécutant différentes versions de Python, alors vous voudrez probablement utiliser explicitement (c'est-à-dire coder en dur) un numéro de protocole spécifique que toutes ces versions peuvent lire (les versions ultérieures peuvent généralement lire les fichiers produits par les versions antérieures).

Objets multiples

Alors qu'un fichier pickle puede peuvent contenir n'importe quel nombre d'objets marinés, comme le montrent les échantillons ci-dessus, lorsqu'il y en a un nombre inconnu, il est souvent plus facile de les stocker tous dans une sorte de récipient de taille variable, comme un list , tuple ou dict et les écrire tous dans le fichier en un seul appel :

tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')

et restaurer la liste et tout ce qu'elle contient plus tard avec :

with open('tech_companies.pkl', 'rb') as input:
    tech_companies = pickle.load(input)

L'avantage principal est que vous n'avez pas besoin de savoir combien d'instances d'objets sont sauvegardées pour les recharger plus tard (bien que le faire sans cette information ). es possible, cela nécessite un code légèrement spécialisé). Voir les réponses à la question connexe Sauvegarde et chargement de plusieurs objets dans un fichier pickle ? pour plus de détails sur les différentes manières de procéder. Personnellement I comme celui de @Lutz Prechelt réponse le meilleur. Le voici adapté aux exemples ici :

class Company:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def pickled_items(filename):
    """ Unpickle a file of pickled data. """
    with open(filename, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

print('Companies in pickle file:')
for company in pickled_items('company_data.pkl'):
    print('  name: {}, value: {}'.format(company.name, company.value))

1 votes

C'est rare pour moi car j'imaginais qu'il y aurait un moyen plus simple de sauvegarder un objet... Quelque chose comme 'saveobject(company1,c : \mypythonobjects )

4 votes

@Peterstone : Si vous ne vouliez stocker qu'un seul objet, vous n'auriez besoin que de la moitié du code de mon exemple. Je l'ai délibérément écrit de cette façon pour montrer comment plus d'un objet pouvait être sauvegardé dans (et plus tard relu depuis) le même fichier.

1 votes

@Peterstone, il y a une très bonne raison pour la séparation des responsabilités. De cette façon, il n'y a pas de limite à l'utilisation des données issues du processus de décapage. Vous pouvez les stocker sur un disque ou les envoyer par une connexion réseau.

58voto

Mike McKerns Points 965

Je pense que c'est une hypothèse assez forte de supposer que l'objet est un class . Et si ce n'est pas un class ? Il y a aussi l'hypothèse que l'objet n'a pas été défini dans l'interpréteur. Et s'il était défini dans l'interpréteur ? Et si les attributs étaient ajoutés dynamiquement ? Lorsque des attributs sont ajoutés à certains objets python, les objets __dict__ après la création, pickle ne respecte pas l'ajout de ces attributs (c'est-à-dire qu'elle "oublie" qu'ils ont été ajoutés, car pickle sérialise par référence à la définition de l'objet).

Dans tous ces cas, pickle y cPickle peut vous faire échouer terriblement.

Si vous cherchez à économiser un object (créé arbitrairement), où vous avez des attributs (soit ajoutés dans la définition de l'objet, soit après) votre meilleure chance est d'utiliser dill qui permet de sérialiser pratiquement n'importe quoi en python.

Nous commençons par une classe

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[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.
>>> import pickle
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> with open('company.pkl', 'wb') as f:
...     pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
... 
>>> 

Maintenant, éteignez, et redémarrez...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[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.
>>> import pickle
>>> with open('company.pkl', 'rb') as f:
...     company1 = pickle.load(f)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
    return Unpickler(file).load()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
    klass = self.find_class(module, name)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
    klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'Company'
>>> 

Oups pickle ne peut pas le supporter. Essayons dill . Nous ajouterons un autre type d'objet (une lambda ) pour faire bonne mesure.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[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.
>>> import dill       
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> with open('company_dill.pkl', 'wb') as f:
...     dill.dump(company1, f)
...     dill.dump(company2, f)
... 
>>> 

Et maintenant, lisez le dossier.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[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.
>>> import dill
>>> with open('company_dill.pkl', 'rb') as f:
...     company1 = dill.load(f)
...     company2 = dill.load(f)
... 
>>> company1 
<__main__.Company instance at 0x107909128>
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>>    

Cela fonctionne. La raison pickle échoue, et dill ne le fait pas, c'est que dill traite __main__ comme un module (pour la plupart), et peut également récupérer les définitions de classes au lieu de les récupérer par référence (comme le fait le module pickle fait). La raison dill peut mariner un lambda c'est qu'il lui donne un nom alors la magie du décapage peut se produire.

En fait, il existe un moyen plus simple de sauvegarder tous ces objets, surtout si vous avez créé un grand nombre d'objets. Il suffit de vider toute la session python, et d'y revenir plus tard.

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[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.
>>> import dill
>>> class Company:
...     pass
... 
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> 
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>> 
>>> dill.dump_session('dill.pkl')
>>> 

Maintenant, fermez votre ordinateur, allez boire un expresso ou autre, et revenez plus tard...

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[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.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>

Le seul inconvénient majeur est que dill ne fait pas partie de la bibliothèque standard de Python. Donc, si vous ne pouvez pas installer un paquet python sur votre serveur, vous ne pouvez pas l'utiliser.

Toutefois, si vous êtes en mesure d'installer les paquets python sur votre système, vous pouvez obtenir la dernière version de l'outil dill con git+https://github.com/uqfoundation/dill.git@master#egg=dill . Et vous pouvez obtenir la dernière version publiée avec pip install dill .

0 votes

Je reçois un TypeError: __new__() takes at least 2 arguments (1 given) lorsqu'on essaie d'utiliser dill (qui semble prometteur) avec un objet plutôt complexe qui inclut un fichier audio.

1 votes

@MikeiLL : Vous obtenez un TypeError quand vous faites quoi, exactement ? C'est généralement le signe que vous avez le mauvais nombre d'arguments lors de l'instanciation d'une instance de classe. Si cela ne fait pas partie du flux de travail de la question ci-dessus, pourriez-vous la poster comme une autre question, me la soumettre par e-mail ou l'ajouter en tant que problème sur le forum de discussion de l dill page github ?

3 votes

Pour ceux qui suivent, voici le Question connexe @MikeLL a posté - d'après la réponse, ce n'était apparemment pas une dill question.

11voto

Anthony Ebert Points 475

Exemple rapide utilisant company1 de votre question, avec python3.

import pickle

# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))

Cependant, comme cette réponse noté, le pickle échoue souvent. Donc vous devriez vraiment utiliser dill .

import dill

# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))

4voto

c0fec0de Points 201

Vous pouvez utiliser anycache pour faire le travail à votre place. Il prend en compte tous les détails :

  • Il utilise aneth comme backend, qui étend le système python pickle pour gérer lambda et toutes les belles fonctionnalités de python.
  • Il stocke différents objets dans différents fichiers et les recharge correctement.
  • Limite la taille du cache
  • Permet de vider le cache
  • Permet le partage d'objets entre plusieurs séries
  • Permet le respect des fichiers d'entrée qui influencent le résultat

Supposons que vous ayez une fonction myfunc qui crée l'instance :

from anycache import anycache

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

@anycache(cachedir='/path/to/your/cache')    
def myfunc(name, value)
    return Company(name, value)

Appels Anycache myfunc à la première fois et conserve le résultat dans un fichier fichier dans cachedir en utilisant un identifiant unique (dépendant du nom de la fonction et de ses arguments) comme nom de fichier. Lors de toute exécution consécutive, l'objet décapé est chargé. Si le cachedir est conservé entre deux exécutions de python, l'objet décapé est repris de l'exécution précédente de python.

Pour toute information complémentaire, voir le documentation

0 votes

Comment utiliser anycache pour sauvegarder plus d'une instance de, disons, une class ou un récipient tel qu'un list (qui n'était pas le résultat de l'appel d'une fonction) ?

1voto

Les versions plus récentes de pandas ont également une fonctionnalité permettant de sauvegarder les cornichons.

Je trouve cela plus facile, par exemple.

pd.to_pickle(object_to_save,'/temp/saved_pkl.pickle' )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X