Contactez-nous

Utilisation des modules struct et pickle pour la sérialisation

Découvrez la sérialisation en Python avec les modules 'struct' (pour les structures de données C) et 'pickle' (pour les objets Python). Apprenez à convertir des objets en séquences d'octets et vice-versa, pour le stockage ou la transmission.

Qu'est-ce que la sérialisation ? Définition et utilité

La sérialisation est le processus de conversion d'un objet (par exemple, une liste, un dictionnaire, une instance de classe) en une séquence d'octets (une chaîne binaire). Cette séquence d'octets peut ensuite être stockée dans un fichier, envoyée sur un réseau, ou transmise à un autre programme.

La désérialisation est le processus inverse : la conversion d'une séquence d'octets en un objet.

La sérialisation est utile pour :

  • Sauvegarder l'état d'un objet : Vous pouvez enregistrer un objet sur le disque, puis le recharger plus tard dans le même état.
  • Transmettre des objets entre programmes : Vous pouvez sérialiser un objet dans un programme, l'envoyer sur un réseau, et le désérialiser dans un autre programme (même s'il s'exécute sur une machine différente).
  • Stocker des données complexes dans des bases de données : Certaines bases de données permettent de stocker des objets sérialisés.
  • Echanger des données entre des programmes écrits dans des langages différents (en utilisant un format de sérialisation standardisé, comme JSON, XML, ou Protocol Buffers. Cependant, ce n'est pas une fonctionnalité native de `struct` et `pickle`).

Python propose plusieurs modules pour la sérialisation, dont les plus courants sont `struct` et `pickle`.

Le module struct : conversion entre Python et structures C

Le module `struct` permet de convertir des données Python (entiers, flottants, chaînes) en une représentation binaire (séquence d'octets) et inversement, en utilisant des formats inspirés des structures de données du langage C.

Il est principalement utilisé pour :

  • Interagir avec du code C (par exemple, appeler des fonctions C à partir de Python, ou lire/écrire des fichiers binaires générés par du code C).
  • Traiter des données binaires structurées (par exemple, des formats de fichiers binaires spécifiques).
  • Optimiser le stockage de données numériques (plus compact que le texte).

Les fonctions principales de `struct` sont :

  • `pack(format, v1, v2, ...)` : Convertit des valeurs Python en une chaîne d'octets selon un format donné.
  • `unpack(format, buffer)` : Convertit une chaîne d'octets en valeurs Python selon un format donné.
  • `calcsize(format)` : Retourne la taille (en octets) de la chaîne d'octets résultant de `pack` avec le format donné.

`format` est une chaîne de caractères qui spécifie le format des données (type, taille, ordre des octets). Par exemple :

  • `'i'` : entier signé (4 octets)
  • `'f'` : flottant simple précision (4 octets)
  • `'d'` : flottant double précision (8 octets)
  • `'h'` : entier court signé (2 octets)
  • `'H'` : entier court non signé (2 octets)
  • `'s'` : chaîne de caractères (précédée de sa taille)
  • `'>'` : big-endian (ordre des octets)
  • `'<'` : little-endian (ordre des octets)
  • Et bien d'autres... (voir la documentation de `struct` pour la liste complète)

Exemples d'utilisation de struct.pack() et struct.unpack()

Conversion d'un entier et d'un flottant en une chaîne d'octets (big-endian) :

import struct

entier = 10
flottant = 3.14

# '>i' : entier signé, 4 octets, big-endian
# 'f' : float, 4 octets.
octets = struct.pack('>if', entier, flottant)  # 'i' : entier, 'f' : flottant
print(octets)  # Affiche quelque chose comme b'\x00\x00\x00\n@I\x0f\xdb'

#Opération inverse:
taille = struct.calcsize('>if')
print(taille) #8 octets

#On décode:
resultat = struct.unpack('>if', octets)
print(resultat) #(10, 3.140000104904175)

Conversion d'une chaîne d'octets en un tuple d'entiers (little-endian) :

import struct

octets = b'\x01\x00\x02\x00\x03\x00'

# '<' : little-endian
# 'h' : entier court signé (2 octets)
valeurs = struct.unpack('

Il est crucial de bien comprendre les chaînes de format pour utiliser `struct` correctement. Une erreur dans le format peut entraîner des résultats incorrects ou des erreurs.

Le module pickle : sérialisation d'objets Python

Le module `pickle` permet de sérialiser et de désérialiser des *objets Python arbitraires*. Il est beaucoup plus puissant et flexible que `struct`, car il peut gérer des objets complexes (listes, dictionnaires, instances de classes, etc.) sans que vous ayez à spécifier un format binaire détaillé.

Les fonctions principales de `pickle` sont :

  • `dump(obj, file)` : Sérialise l'objet `obj` et l'écrit dans le fichier `file` (qui doit être ouvert en mode écriture binaire).
  • `dumps(obj)` : Sérialise l'objet `obj` et retourne la chaîne d'octets résultante.
  • `load(file)` : Lit un objet sérialisé depuis le fichier `file` (qui doit être ouvert en mode lecture binaire) et le retourne.
  • `loads(bytes_object)` : Lit un objet sérialisé depuis un objet `bytes` et le retourne.

Exemple :

import pickle

donnees = {
    'nom': 'Alice',
    'age': 30,
    'scores': [85, 92, 78]
}

# Sérialisation et écriture dans un fichier
with open('donnees.pickle', 'wb') as fichier:
    pickle.dump(donnees, fichier)

# Lecture et désérialisation
with open('donnees.pickle', 'rb') as fichier:
    donnees_chargees = pickle.load(fichier)

print(donnees_chargees)  # Affiche le dictionnaire original

Dans cet exemple, un dictionnaire Python est sérialisé avec `pickle.dump` et écrit dans un fichier. Le fichier est ensuite lu et le dictionnaire est reconstitué avec `pickle.load`.

Limitations et avertissements concernant pickle

Bien que `pickle` soit très pratique, il a des limitations et des inconvénients importants :

  • Sécurité : Ne désérialisez *jamais* des données provenant d'une source non fiable avec `pickle`. Il est possible de construire des données pickle malveillantes qui exécuteront du code arbitraire lors de la désérialisation. `pickle` n'est *pas* sécurisé.
  • Compatibilité : Le format de `pickle` est spécifique à Python. Vous ne pouvez pas utiliser `pickle` pour échanger des données avec des programmes écrits dans d'autres langages. De plus, le format de `pickle` peut changer entre les versions de Python, ce qui peut poser des problèmes de compatibilité à long terme.
  • Lisibilité : Les données sérialisées avec `pickle` ne sont pas lisibles par un humain.
  • Types supportés : `pickle` ne peut pas sérialiser tous les types d'objets Python (par exemple, les objets qui représentent des connexions réseau ou des fichiers ouverts ne sont généralement pas sérialisables).

Pour ces raisons, il est souvent préférable d'utiliser d'autres formats de sérialisation, comme JSON (pour les données textuelles) ou des bibliothèques comme `protobuf` (pour les données binaires), lorsque la sécurité, la portabilité ou la lisibilité sont importantes.

`pickle` reste utile pour des usages spécifiques, comme la sauvegarde temporaire de l'état d'un objet Python, ou la communication entre des programmes Python de confiance.

Comparaison rapide : struct vs. pickle

Caractéristiquestructpickle
ObjectifConversion entre Python et structures C (format binaire fixe)Sérialisation d'objets Python arbitraires
Types de donnéesTypes de base (entiers, flottants, chaînes)Presque tous les objets Python
FormatDéfini par une chaîne de formatPropre à Python (et potentiellement non compatible entre les versions)
SécuritéSûr (si le format est correct)Non sûr avec des données non fiables
LisibilitéNon lisible par un humainNon lisible par un humain
Cas d'utilisation typiquesInteraction avec du code C, traitement de fichiers binaires structurésSauvegarde d'objets Python, communication inter-processus (entre processus Python de confiance)