Comment sérialiser des objets Python

Si vous devez sérialiser un objet Python dans un flux d’octets afin de pouvoir l’enregistrer dans un fichier, le stocker dans une base de données ou le transmettre via une connexion réseau, alors cet article est pour vous.

L’approche la plus courante pour sérialiser les données est d’utiliser le module pickle. Pour charger un objet dans un fichier, procédez comme suit:

import pickle

objet = ...   # un objet Python
f = open('fichier', 'wb')
pickle.dump(objet, f)

 

Pour charger un objet dans une chaîne de caractères, utilisez pickle.dumps():

s = pickle.dumps(objet)

Pour recréer un objet à partir d’un flux d’octets, utilisez les fonctions pickle.load() ou pickle.loads(). Par exemple:

# Restaurer des données picklées à partir d'un fichier
f = open(fihicer, 'rb')
donnees = pickle.load(f)

# Restaurer des données picklées à partir d'une chaîne de caractères
donnees = pickle.loads(s)

 

Pour la plupart des programmes, l’utilisation des fonctions dump() et load() est tout ce dont vous avez besoin pour utiliser efficacement pickle. Ce module fonctionne simplement avec la plupart des types de données Python et des instances de classes définies par l’utilisateur.

Si vous travaillez avec n’importe quel type de bibliothèque qui vous permet de faire des choses telles que sauvegarder/restaurer des objets Python dans des bases de données ou transmettre des objets sur le réseau, il y a de fortes chances que pickle soit utilisé.

pickle est un codage de données auto-descriptif spécifique à Python. Avec l’auto-description, les données sérialisées contiennent des informations relatives au début et à la fin de chaque objet ainsi que des informations sur son type.

Ainsi, vous n’avez pas à vous soucier de la définition des enregistrements – cela fonctionne sans problème. Par exemple, si vous utilisez plusieurs objets, vous pouvez faire ceci:

>>> import pickle
>>> f = open('fichier', 'wb')
>>> pickle.dump([1, 2, 3, 4], f)
>>> pickle.dump('hello', f)
>>> pickle.dump({'Google', 'Microsoft', 'HP'}, f)
>>> f.close()
>>> f = open('fichier', 'rb')
>>> pickle.load(f)
[1, 2, 3, 4]
>>> pickle.load(f)
'hello'
>>> pickle.load(f)
{'Google', 'Microsoft', 'HP'}
>>>

 

Vous pouvez pickler des fonctions, des classes et des instances, mais les données qui en résultent ne codent que les références de nom aux objets de code associés. Par exemple:

>>> import math
>>> import pickle
>>> pickle.dumps(math.cos)
b'\x80\x03cmath\ncos\nq\x00.'
>>>

Lorsque les données sont décomposées, on suppose que toutes les sources requises sont disponibles. Les modules, les classes et les fonctions seront automatiquement importés selon les besoins.

Pour les applications où les données Python sont partagées entre les interpréteurs sur différentes machines, il s’agit d’un problème de maintenance potentiel, car toutes les machines doivent avoir accès au même code source.

pickle.load() ne devrait jamais être utilisé sur des données non fiables. Comme effet secondaire du chargement, pickle chargera automatiquement les modules et créera des instances.

Cependant, un malfaiteur qui sait comment fonctionne Pickle peut créer des données “malformées” qui font que Python exécute des commandes système arbitraires.

Il est donc essentiel que le pickle ne soit utilisé qu’en interne avec les interprèteurs qui ont une certaine capacité à s’authentifier mutuellement.

Certains types d’objets ne peuvent pas être picklés. Il s’agit généralement d’objets qui impliquent une sorte d’état de système externe, tels que des fichiers ouverts, des connexions réseau ouvertes, des threads, des processus, des cadres de pile, etc.

Les classes définies par l’utilisateur peuvent parfois contourner ces limitations en fournissant les méthodes __getstate__() et __setstate__(). Si défini, pickle.dump() appellera __getstate__() pour obtenir un objet qui peut être picklé.

De même, __setstate__() sera invoqué lors du déblocage. Pour illustrer ce qui est possible, voici une classe qui définit en interne un thread mais qui peut toujours être sérialisé ou dé-sérialisé:

# compteur.py
import time
import threading

class Compteur:
    def __init__(self, n):
        self.n = n
        self.thr = threading.Thread(target=self.run)
        self.thr.daemon = True
        self.thr.start()

    def run(self):
        while self.n > 0:
            print('T-moins', self.n)
            self.n -= 1
            time.sleep(5)

    def __getstate__(self):
        return self.n

    def __setstate__(self, n):
        self.__init__(n)

 

Essayez l’expérience suivante avec l:

>>> import compteur
>>> c = compteur.Compteur(30)
>>> T-moins 30
T-moins 29
T-moins 28
...

>>> # Après quelques moments
>>> f = open('cetat.p', 'wb')
>>> import pickle
>>> pickle.dump(c, f)
>>> f.close()

 

Maintenant, quittez Python et essayez ceci après le redémarrage:

>>> f = open('cetat.p', 'rb')
>>> pickle.load(f)
compteur.Compteur object at 0x10069e2d0>
T-moins 19
T-moins 18
...

Vous devriez voir le thread actif de nouveau, reprenant là où il s’était arrêté lorsque vous l’avez picklé pour la première fois.

pickle n’est pas un encodage particulièrement efficace pour les grandes structures de données telles que les tableaux binaires créés par des bibliothèques comme le module array ou numpy.

Si vous déplacez de grandes quantités de données de tableau, il peut être préférable d’enregistrer tout simplement les données de tableau en masse dans un fichier ou d’utiliser un codage plus standardisé, tel que HDF5 (supporté par des bibliothèques tierces).

En raison de sa nature spécifique à Python et de son attachement au code source, vous ne devriez probablement pas utiliser pickle comme format de stockage à long terme.

Par exemple, si le code source change, toutes vos données stockées risquent de se briser et de devenir illisibles.

Franchement, pour stocker des données dans des bases de données et des archives, il est probablement préférable d’utiliser un codage de données plus standard, tel que XML, CSV ou JSON.

Ces encodages sont plus standardisés, supportés par de nombreux langages différents, et plus susceptibles d’être mieux adaptés aux changements dans votre code source.

Enfin et surtout, sachez qu’il existe une grande variété d’options et de cas d’angle délicats.

Pour les utilisations les plus courantes, vous n’avez pas besoin de vous en soucier, mais un coup d’oeil à la documentation officielle devrait être nécessaire si vous allez construire une application significative qui utilise pickle pour la sérialisation.

LAISSER UN COMMENTAIRE

Please enter your comment!
Please enter your name here