Enregistrement D'un objet (persistance des données)

J'ai créé un objet comme ceci:

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

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

157
demandé sur H. Tao 2010-12-25 12:02:03

3 réponses

Vous pouvez utiliser le module 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 écrire un utilitaire simple comme la suivante, qui ouvre un fichier et é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 si populaire, j'aimerais aborder quelques sujets d'utilisation légèrement avancés.

cPickle (ou _pickle vs pickle

Il est presque toujours préférable d'utiliser le cPickle module plutôt que de pickle, car l'ancien est écrit en C et est beaucoup plus rapide. Il y a quelques différences subtiles entre eux, mais dans la plupart des situations, ils sont équivalents et la version C fournira des performances considérablement supérieures. Passer à cela ne pouvait pas être plus facile, il suffit de changer l'instruction import à ceci:

import cPickle as pickle

En Python 3, cPickle a été renommé _pickle, mais cela n'est plus nécessaire puisque la pickle module maintenant c'est fait automatiquement-voir Ce différence entre pickle et _pickle dans python 3?.

Le résumé est que vous pouvez utiliser quelque chose comme ce qui suit pour vous assurer que votre code utilisera toujours la version C quand elle est disponible en 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 . "Protocol version 0" est ASCII et donc "lisible par l'homme". Les Versions > 1 sont binaires et le plus élevé disponible dépend de la version de Python utilisée. La valeur par défaut dépend également de la version Python. Dans Python 2, La valeur par défaut était la version du protocole 0, mais dans Python 3.6, c'est la version du protocole 3. En Python 3.x le module avait un pickle.DEFAULT_PROTOCOL ajouté, mais cela n'existe pas dans Python 2.

Heureusement, il y a un raccourci pour écrire pickle.HIGHEST_PROTOCOL dans chaque appel (en supposant que c'est ce que vous voulez, et vous le faites habituellement)-utilisez simplement le nombre littéral -1. Ainsi, au lieu de l'écriture:

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

, Vous pouvez simplement écrire:

pickle.dump(obj, output, -1)

De toute façon, vous n'auriez à spécifier le protocole qu'une seule fois si vous avez créé un objet Pickler à utiliser dans plusieurs opérations de pickle:

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

Objets Multiples

Alors qu'un fichier de cornichons peut contenir n'importe quel nombre d'objets décapés, comme indiqué dans les exemples ci-dessus, quand il y en a un nombre inconnu, il est souvent plus facile de les stocker tous dans une sorte de conteneur de taille variable, comme un list, tuple, ou dict et les écrire 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 restaurez 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 majeur est que vous n'avez pas besoin de savoir combien d'instances d'objets sont enregistrées pour les charger plus tard (bien que cela soit possible sans cette information , cela nécessite un code légèrement spécialisé). Voir les réponses à la question connexe enregistrement et chargement de plusieurs objets dans le fichier pickle? pour plus de détails sur différentes façons de le faire. Personnellement j'aime la réponse DE @Lutz Prechelt la meilleure. 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))
324
répondu martineau 2018-05-21 12:27:19

Je pense que c'est assez forte hypothèse de supposer que l'objet est un class. Si ce n'est pas un class? Il y a aussi l'hypothèse que l'objet n'est pas définie dans l'interpréteur. Que faire si elle a été définie dans l'interprète? Aussi, si les attributs ont été ajoutés dynamiquement? Lorsque certains objets python ont des attributs ajoutés à leur __dict__ après la création, pickle ne respecte pas l'ajout de ces attributs (c'est-à-dire qu'il "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 et cPickle pouvez vous échouez horriblement.

Si vous cherchez à enregistrer un object (Créé arbitrairement), où vous avez des attributs( soit ajoutés dans la définition de l'objet, soit par la suite) ... Votre meilleur pari est d'utiliser dill, qui peut sérialiser presque n'importe quoi en python.

On commence 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, arrêtez 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 manipuler. Essayons dill. Nous allons ajouter un autre type d'objet (a 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 fichier.

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

Ça marche. La raison pour laquelle pickle échoue, et dill ne le fait pas, est que dill traite __main__ comme un module (pour la plupart), et peut également décaper les définitions de classe au lieu de décaper par référence (comme pickle fait). La raison pour laquelle dill peut décaper un lambda est qu'il lui donne un nom... alors la magie du décapage peut se produire.

En fait, il y a un moyen plus facile de sauver tout ça les objets, surtout si vous avez beaucoup d'objets que vous avez créés. Il suffit de vider toute la session python, et 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 éteignez votre ordinateur, allez déguster 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 python. Donc, si vous ne pouvez pas installer un paquet python sur votre serveur, vous ne pouvez pas l'utiliser.

Toutefois, si vous pouvez installer des paquets python sur votre système, vous pouvez obtenir le dernier dill avec git+https://github.com/uqfoundation/dill.git@master#egg=dill. Et vous pouvez obtenir la dernière version publiée avec pip install dill.

40
répondu Mike McKerns 2016-05-13 00:54:58

, Vous pouvez utiliser anycache pour faire le travail pour vous. Il considère tous les détails:

  • , Il utilise aneth, comme backend, qui étend le module python pickle pour gérer lambda et tous les nice caractéristiques python.
  • Il stocke différents objets dans différents fichiers et les Recharge correctement.
  • limite la taille du cache
  • permet d'effacer le cache
  • permet le partage d'objets entre plusieurs exécutions
  • permet le respect des fichiers d'entrée qui influencer le résultat

En supposant que vous avez 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)

Anycache appelle myfunc à la première fois et nettoie le résultat à un fichier dans cachedir en utilisant un identifiant unique (en fonction 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 les exécutions python, l'objet décapé est extrait de l'exécution Python précédente.

Pour plus de détails voir la documentation

2
répondu c0fec0de 2017-11-19 20:27:41