Persistance des données

La sérialisation (en anglais serialization ou marshalling) est le processus par lequel on transforme des données présentes en mémoire en une suite de codes qui pourra :

  • être enregistrée dans un fichier. Cela permet de rendre persistant un « objet informatique » (il survivra à un redémarrage du système, au terme duquel on pourra le reconstituer à l’identique) ;
  • être transportée sur un réseau. Cela permet de transférer à un ordinateur distant un « objet informatique » (par exemple via un réseau).

L’opération réciproque (le décodage des informations pour créer une copie conforme à l’original), s’appelle la désérialisation (deserialization ou unmarshalling).

Il y a plusieurs façon de sérialiser des données :

  • à l’aide de Module json pour des « données simples » ;
  • à l’aide de Module pickle pour des « objets Python » plus complexes ;
  • à l’aide de XML (proche de JSON, mais plus verbeux, plus riche et plus complexe) ;
  • il arrive souvent que les données soient en outre compressées, afin d’économiser la place sur le disque dur et/ou la bande passante sur les réseaux. Voir Compresser avec gzip.

Module json

JSON (JavaScript Object Notation) est un format de données textuelles permettant de représenter des informations structurées. JSON est utilisable par de nombreux langages de programmation. C’est une alternative moins verbeuse (mais également moins « parlante ») à XML.

En Python, la structure de base sera une liste, ou un dictionnaire dont les clés devront impérativement être des chaînes de caractères. Les valeurs seront uniquement des chaînes, des nombres, les valeurs True, False ou None (traduit en null en terminologie JSON), ou d’autres listes ou dictionnaires respectant les mêmes contraintes. Le résultat après conversion sera une chaîne de caractères représentant cette structure au format JSON. Cette chaîne pourra être enregistrée dans un fichier ou transmise à un autre ordinateur via le réseau (c’est surtout dans ce dernier contexte que JSON est utile, cf. AJAJ versus AJAX).

Remarque 1 : le stockage JSON n’est possible que pour certains objets Python. Voir par ici pour les plus curieux...

Remarque 2 : JSON est incapable de figer l’ordre des données (dictionnaire Python). Si c’est un point important (utilisation d’OrderedDict), il faut impérativement recourir à Pickle...

json.dump et json.dumps : sérialisation avec JSON

Conversion en chaîne de caractères pour envoi via réseau

>>> personne = { "id" : 1, "nom" : "DUPONT", "prénom" : "Jean", "âge" : 25, "marié" : False, "conjoint" : None }
>>> import json
>>> json.dumps(personne)
'{"nom": "DUPONT", "\\u00e2ge": 25, "mari\\u00e9": false, "conjoint": null, "pr\\u00e9nom": "Jean", "id": 1}'

Enregistrement dans un fichier

>>> with open("test.js","w") as f:
... json.dump(personne,f)
...

json.load et json.loads : désérialisation avec JSON

Conversion d’une chaîne de caractères contenant du code JSON en structure Python :

>>> import json
>>> json.loads('[ 1, 2, { "a":1, "b":[1,2], "c":true, "d":null}]')
[1, 2, {'a': 1, 'b': [1, 2], 'c': True, 'd': None}]

Chargement depuis un fichier :

>>> with open("test.js","r") as f:
... d = json.load(f)
...
>>> print(d)
{'conjoint': None, 'id': 1, 'marié': False, 'nom': 'DUPONT', 'prénom': 'Jean', 'âge': 25}

Tableau récapitulatif et astuce mnémotechnique

Fonction Travaille avec des chaînes Travaille avec des fichiers
Lit json.loads json.load
Écrit json.dumps json.dump

Astuce : le s dans json.loads et json.dumps fait référence aux chaînes de caractères (string en anglais).

>> Tout savoir sur la bibliothèque Python consacrée à JSON.

Module pickle

Le module pickle de Python 3 implémente une meilleure sérialisation que ce que l’on peut obtenir avec Module json : il permet de transformer en suite de bits des « objets » Python complexes. Seul Python peut désérialiser le résultat, Pickle n’est donc pas un bon moyen d’échanger des données entre des programmes écrits dans différents langages.

pickle.dump et pickle.dumps : sérialisation avec Pickle

Conversion en « chaîne de caractères binaires » (type bytes) pour envoi via réseau :

>>> personne = { "id" : 1, "nom" : "DUPONT", "prénom" : "Jean", "âge" : 25, "marié" : False, "conjoint" : None }
>>> import pickle
>>> pickle.dumps(personne)
b'\x80\x03}q\x00(X\x03\x00\x00\x00nomq\x01X\x06\x00\x00\x00DUPONTq\x02X\x04\x00\x00\x00\xc3\xa2geq\x03K\x19X\x06
\x00\x00\x00mari\xc3\xa9q\x04\x89X\x08\x00\x00\x00conjointq\x05NX\x07\x00\x00\x00pr\xc3\xa9nomq\x06X\x04\x00\x00
\x00Jeanq\x07X\x02\x00\x00\x00idq\x08K\x01u.'

Enregistrement dans un fichier :

>>> with open("test.pkl", "wb") as f:
... pickle.dump(personne, f)
...

Remarque : l’option b d’ouverture de fichier en mode binaire est importante !

pickle.load et pickle.loads : désérialisation avec Pickle

Conversion d’un objet bytes (« chaîne de caractères binaires ») en structure Python :

>>> pickle.loads(b'\x80\x03]q\x00(K\x01K\x02}q\x01(X\x01\x00\x00\x00aq\x02K\x01X\x01\x00\x00\x00cq\x03\x88X
\x01\x00\x00\x00bq\x04]q\x05(K\x01K\x02eX\x01\x00\x00\x00dq\x06Nue.')
[1, 2, {'a': 1, 'b': [1, 2], 'c': True, 'd': None}]

Chargement depuis un fichier :

>>> with open("test.pkl","rb") as f:
... d = pickle.load(f)
...
>>> print(d)
{'conjoint': None, 'id': 1, 'marié': False, 'nom': 'DUPONT', 'prénom': 'Jean',
'âge': 25}

Remarque : l’option b d’ouverture de fichier en mode binaire est importante !

Tableau récapitulatif et astuce mnémotechnique

Fonction Travaille avec des chaînes binaires (bytes) Travaille avec des fichiers
Lit pickle.loads pickle.load
Écrit pickle.dumps pickle.dump

Astuce : le s dans pickle.loads et pickle.dumps fait référence aux chaînes de caractères (string en anglais).

>> Tout savoir sur la bibliothèque Python consacrée à Pickle.

Compresser avec gzip

Il peut être utile de compresser certains « objets » Python volumineux (structures textuelles de grande taille, comme par exemple une base de données cartographiques libres issue du projet OpenStreeMap - la plus grosse dépasse les 30 Go !). Plusieurs bibliothèques permettent cela, entre autres zipfile et gzip. La 1ère est plus complète et gère de nombreux algorithmes de compression, mais son emploi est bien plus complexe que la seconde. C’est donc elle (gzip) qu’on illustrera dans la suite.

Note : cette fiche suppose que vous savez à quoi sert le module pickle. Voir Module pickle.

Utilisation conjointe de gzip.open et pickle.dumps pour compresser des données sérialisées

>>> import gzip, pickle
>>> d={}
>>> for i in range(100000):
...    d[i]=chr(i % 26 + 65)*100
# NE SURTOUT PAS faire print(d), ce serait trèèèèèès long !
>>> with gzip.open("test.pkz", "wb") as f:
...     f.write(pickle.dumps(d))

L’effet sur la taille des données est drastique (division par 10 !) :

>>> import sys
>>> sys.getsizeof(d)
6291736
>>> d2=open("test.pkz", "rb").read()
>>> sys.getsizeof(d2)
605077

Utilisation conjointe de gzip.open et pickle.loads pour décompresser des données sérialisées

>>> with gzip.open("test.pkz","rb") as f:
...
datas = pickle.loads(f.read())
>>> datas == d
True

Ressources sur l’utilisation du module zipfile

Note : les liens donnés ci-dessous concernent Python 2, il faudra donc adapter le contenu à Python 3 !