Python: Le module pickle

Comme son nom l'indique, enfin non, pas vraiment, le module pickle permet de sauvegarder dans un fichier, au format binaire,  n'importe quel objet Python.

En clair, si pour une raison quelconque, dans un script Python, vous avez besoin de sauvegarder, temporairement ou même de façon plus pérenne, le contenu d'un objet Python comme une liste, un dictionnaire, un tuple etc etc ... au lieu d'utiliser une base de données ou un simple fichier texte, le module pickle est fait pour ça.

Il permet de stocker et de restaurer un objet Python tel quel sans aucune manipulation supplémentaire.

C'est vraiment super pratique.

Il fonctionne comme le module json mais n'est pas limité à un seul format d'objet.

Exemples:

>>> import pickle
>>> import string
>>> L = list(string.ascii_letters)
>>> print(L)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(L, f1)
>>> with open('mypicklefile', 'r') as f1:
    f1.read()

    
'€\x03]q\x00(X\x01\x00\x00\x00aq\x01X\x01\x00\x00\x00bq\x02X\x01\x00\x00\x00cq\x03X\x01\x00\x00\x00dq\x04X\x01\x00\x00\x00eq\x05X\x01\x00\x00\x00fq\x06X\x01\x00\x00\x00gq\x07X\x01\x00\x00\x00hq\x08X\x01\x00\x00\x00iq\tX\x01\x00\x00\x00jq\nX\x01\x00\x00\x00kq\x0bX\x01\x00\x00\x00lq\x0cX\x01\x00\x00\x00mq\nX\x01\x00\x00\x00nq\x0eX\x01\x00\x00\x00oq\x0fX\x01\x00\x00\x00pq\x10X\x01\x00\x00\x00qq\x11X\x01\x00\x00\x00rq\x12X\x01\x00\x00\x00sq\x13X\x01\x00\x00\x00tq\x14X\x01\x00\x00\x00uq\x15X\x01\x00\x00\x00vq\x16X\x01\x00\x00\x00wq\x17X\x01\x00\x00\x00xq\x18X\x01\x00\x00\x00yq\x19X\x01\x00\x00\x00zq\x1aX\x01\x00\x00\x00Aq\x1bX\x01\x00\x00\x00Bq\x1cX\x01\x00\x00\x00Cq\x1dX\x01\x00\x00\x00Dq\x1eX\x01\x00\x00\x00Eq\x1fX\x01\x00\x00\x00Fq X\x01\x00\x00\x00Gq!X\x01\x00\x00\x00Hq"X\x01\x00\x00\x00Iq#X\x01\x00\x00\x00Jq$X\x01\x00\x00\x00Kq%X\x01\x00\x00\x00Lq&X\x01\x00\x00\x00Mq\'X\x01\x00\x00\x00Nq(X\x01\x00\x00\x00Oq)X\x01\x00\x00\x00Pq*X\x01\x00\x00\x00Qq+X\x01\x00\x00\x00Rq,X\x01\x00\x00\x00Sq-X\x01\x00\x00\x00Tq.X\x01\x00\x00\x00Uq/X\x01\x00\x00\x00Vq0X\x01\x00\x00\x00Wq1X\x01\x00\x00\x00Xq2X\x01\x00\x00\x00Yq3X\x01\x00\x00\x00Zq4e.'
>>> OL = None
>>> print(OL)
None
>>> with open('mypicklefile', 'rb') as f1:
    OL = pickle.load(f1)
>>> type(OL)
<class 'list'>
>>> print(OL)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> L == OL
True
>>>

Dans l'exemple ci-dessus, j'ai créé une liste "L" contenant toutes les lettres de l'alphabet.
J'ai sauvegardé mon objet "L" (liste Python) dans un fichier "mypicklefile" grâce à la méthode dump du module pickle.
Précision importante, le module pickle écrit les données uniquement dans un fichier ouvert en mode binaire.
J'ai ouvert le fichier et afficher son contenu pour bien montrer que pickle écrit les données au format binaire.
J'ai créé un nouvel objet "OL" ayant None comme valeur (cette étape n'est pas obligatoire - uniquement pour montrer que l'objet "OL" n'existait pas auparavant).
J'ai ensuite chargé le contenu du fichier "mypicklefile" dans mon objet "OL" grâce à la méthode load du module pickle.
J'affiche le contenu de la nouvelle liste "OL" et le test d'égalité de l'objet "L" et "OL" pour bien montrer que les deux objets sont bien identiques.

La liste "OL" peut être modifiée (ajout, modification, suppression des valeurs) et à nouveau sauvegardée dans le fichier grâce à la méthode dump du module pickle pour une prochaine utilisation.

Petite précision, pour la méthode dump, le fichier doit être ouvert en mode 'wb' afin d'écraser le contenu précédent.
Si le fichier est ouvert en mode 'ab', les données sont écrites à la fin du fichier mais la méthode load récupère les données au début du fichier.
De toute manière, le mode append n'a aucun intérêt pour ce genre de stockage de données. On dump et on load l'intégralité du contenu d'un objet.

Et ça fonctionne pour tous types d'objets

Avec un tuple

>>> import pickle
>>> T = tuple(string.ascii_letters)
>>> T
('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(T, f1)
>>> with open('mypicklefile', 'rb') as f1:
    OT = pickle.load(f1)
>>> OT
('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
>>>

Avec un dictionnaire

>>> import pickle
>>> I = list(range(52))
>>> L = list(string.ascii_letters)
>>> D = {i: l for i, l in zip(I, L)}
>>> D
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(D, f1)
>>> with open('mypicklefile', 'rb') as f1:
    OD = pickle.load(f1)
>>> OD
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>>

Avec un deque (du module collections)

>>> import pickle
>>> from collections import deque
>>> L = list(string.ascii_letters)
>>> L
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
>>> DE = deque(L)
>>> DE
deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'])
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(DE, f1)
>>> with open('mypicklefile', 'rb') as f1:
    ODE = pickle.load(f1)
>>> ODE
deque(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'])
>>>

Précision importante, la méthode load importe automatiquement l'objet nécessaire au chargement des données.
C'est à dire que dans l'exemple ci-dessus, l'objet deque du module collections n'a pas besoin d'être importé pour être correctement chargé via la méthode load. Il doit bien évidement être disponible dans la liste des modules de Python.

Et enfin, avec un objet perso

>>> class MyObject():
    def __init__(self):
        self.un = 1
        self.deux = 2
        self.trois = 3
    def __repr__(self):
        return "{}, {}, {}".format(self.un, self.deux, self.trois)
    def __str__(self):
        return "un: {}\ndeux: {}\ntrois: {}".format(self.un, self.deux, self.trois)

    
>>> A = MyObject()
>>> A
1, 2, 3
>>> print(A)
un: 1
deux: 2
trois: 3
>>> with open('mypicklefile', 'wb') as f1:
    pickle.dump(A, f1)
>>> with open('mypicklefile', 'rb') as f1:
    B = pickle.load(f1)
>>> B
1, 2, 3
>>> print(B)
un: 1
deux: 2
trois: 3
>>>

Dans ce cas, la class MyObject() doit être disponible pour pouvoir être utilisée via la méthode load sinon, une erreur "AttributeError" est levée.

Associé au module tempfile, nous avons un système complet de sauvegarde temporaire d'objets utilisable dans n'importe quel script Python.

Etiquettes: