J'ai créé un objet comme ceci :
company1.name = 'banana'
company1.value = 40
Je voudrais sauvegarder cet objet. Comment puis-je le faire ?
J'ai créé un objet comme ceci :
company1.name = 'banana'
company1.value = 40
Je voudrais sauvegarder cet objet. Comment puis-je le faire ?
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')
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
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).
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))
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 )
@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.
@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.
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
.
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.
@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 ?
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.
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"))
Vous pouvez utiliser anycache pour faire le travail à votre place. Il prend en compte tous les détails :
pickle
pour gérer lambda
et toutes les belles fonctionnalités de python.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
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.
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.