# `pickle` — Sérialisation d'objets Python

Code source : Lib/pickle.py

Le module `pickle` implémente des protocoles binaires de sérialisation et dé-sérialisation d'objets Python. La sérialisation est le procédé par lequel une hiérarchie d'objets Python est convertie en flux d'octets. La désérialisation est l'opération inverse, par laquelle un flux d'octets (à partir d'un binary file ou bytes-like object) est converti en hiérarchie d'objets. Sérialisation (et désérialisation) sont aussi connus sous les termes de pickling, de "marshalling" 1 ou encore de "flattening".

Avertissement

Le module `pickle` n'est pas sécurisé. Ne désérialisez des objets qu'à partir de sources fiables.

Il est possible de produire des données binaires qui exécutent du code arbitraire lors de leur désérialisation. Ne désérialisez jamais des données provenant d'une source non fiable, ou qui pourraient avoir été modifiées.

Pensez au module `hmac` pour signer des données afin de s'assurer qu'elles n'ont pas été modifiées.

Des formats de sérialisation plus sûrs, comme `json`, peuvent se révéler plus adaptés si vous travaillez sur des données qui ne sont pas fiables. Voir Comparaison avec json.

## Relations aux autres modules Python

### Comparaison avec `marshal`

Python possède un module de bas niveau en sérialisation appelé `marshal`, mais en général il est préférable d'utiliser `pickle` pour sérialiser des objets Python. `marshal` existe principalement pour gérer les fichiers Python en `.pyc`.

Le module `pickle` diffère du module `marshal` sur plusieurs aspects :

- Le module `pickle` garde la trace des objets qu'il a déjà sérialisés, pour faire en sorte que les prochaines références à cet objet ne soient pas sérialisées à nouveau. `marshal` ne le fait pas.

  Ça a des implications sur les objets partagés et les objets récursifs. Les objets récursifs sont des objets qui contiennent des références à eux-mêmes. Ceux-ci ne sont pas gérées par marshal : lui donner un objet récursif va le faire planter. Un objet est partagé lorsque que plusieurs références pointent dessus, depuis différents endroits dans la hiérarchie sérialisée. Le module `pickle` repère ces partages et ne stocke ces objets qu'une seule fois. Les objets partagés restent ainsi partagés, ce qui peut être très important pour les objets muables.

- `marshal` ne peut être utilisé pour la sérialisation et l'instanciation de classes définies par les utilisateurs. `pickle` peut sauvegarder et restaurer les instances de classes de manière transparente. Cependant la définition de classe doit être importable et lancée dans le même module et de la même manière que lors de son importation.

- Aucune garantie n'est offerte sur la portabilité du format `marshal` entre différentes versions de Python. Sa fonction première étant la gestion des fichiers `.pyc`, les développeurs se réservent le droit de changer le format de sérialisation de manière non-rétrocompatible si besoin était. Il est garanti que le format `pickle` restera compatible avec les versions futures de Python, pourvu que vous choisissiez un protocole de sérialisation adapté. De plus, il masque les différences entre les types Python 2 et Python 3, pour le cas où il s'agit de désérialiser en Python 3 des données sérialisées en Python 2.

### Comparaison avec `json`

There are fundamental differences between the pickle protocols and JSON (JavaScript Object Notation):

- pickle est un format binaire, tandis que JSON est un format textuel (constitué de caractères Unicode et généralement encodé en UTF-8) ;

- JSON peut être lu par une personne, contrairement à pickle ;

- JSON offre l'interopérabilité avec de nombreux outils en dehors de l'écosystème Python, alors que pickle est propre à Python ;

- Par défaut, JSON n'est capable de sérialiser qu'un nombre limité de types natifs Python, et ne prend pas en charge les classes définies par l'utilisateur. Le format pickle peut représenter une multitude de types d'objets, dont beaucoup automatiquement, grâce à une utilisation fine des possibilités d'introspection de Python ; on peut traiter les cas les plus complexes en implémentant des méthodes de sérialisation propres à une classe ;

- Contrairement à pickle, la désérialisation de données JSON n'ouvre pas en soi une vulnérabilité à l'exécution de code arbitraire.

Voir aussi

Le module `json` de la bibliothèque standard permet la sérialisation et désérialisation au format JSON.

## Format du flux de données

Le format de données employé par `pickle` est propre à Python, avec l'avantage qu'aucune restriction n'est imposée par des standards externes comme JSON ou XDR (qui ne peuvent pas représenter le partage de références). Cependant, cela signifie que des programmes écrits en d'autres langages que Python peuvent échouer à reconstituer les objets sérialisés.

Le format binaire `pickle` est, par défaut, une représentation assez compacte des objets. Il est possible de compresser efficacement les données sérialisées.

Le module `pickletools` contient des outils servant à analyser les flux de données générés par `pickle`. Le code source de `pickletools` contient des commentaires détaillés sur les opcodes employés par les protocoles pickle.

Il existe actuellement 6 protocoles différents pour la sérialisation. Les protocoles portant les numéros les plus grands sont les derniers ajoutés, et nécessitent en conséquence des versions de Python plus récentes.

- Le protocole 0 est le format originel, humainement lisible. Il est rétrocompatible avec les versions les plus anciennes de Python.

- Le protocole 1 est un ancien format binaire, aussi compatible avec les versions anciennes.

- Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307 for information about improvements brought by protocol 2.

- Le protocole 3 a été introduit en Python 3.0. Il gère les objets `bytes`, et ne permet pas la désérialisation par Python 2.x. Il fut le protocole par défaut de Python 3.0 à Python 3.7.

- Le protocole 4 est apparu en Python 3.4. Il prend en charge les objets de très grande taille ainsi que la sérialisation d'une plus grande variété d'objets, et optimise le format. Il est le protocole par défaut depuis Python 3.8. Voir la PEP 3154 pour plus d'informations sur les améliorations apportées par le protocole 4.

- Le protocole 5 a été ajouté en Python 3.8 afin de permettre le transfert des données en marge de la sérialisation elle-même. Il a également accéléré les opérations sur les données à sérialiser. Reportez-vous à la PEP 574 pour les détails relatifs aux améliorations apportées par le protocole 5.

Note

La sérialisation est un problème plus simple que la persistance des données en général. Le module `pickle` lit et écrit des objets fichiers-compatibles, mais ne s'occupe pas du problème de donner un nom à des objets persistants, ni de gérer l'accès par différents processus en parallèle à ces objets. `pickle` se contente de transformer des objets complexes en flux d'octets, et lire ces flux d'octets par la suite pour reconstruire des objets avec la même structure. Si l'on peut bien sûr écrire les flux d'octets dans un fichier, rien n'empêche de les transférer à travers un réseau, ou bien de les stocker dans une base de données. Voir le module `shelve` pour une interface simple qui sérialise et désérialise les objets dans des bases de données de style DBM.

## Interface du module

Pour sérialiser un objet, contenant éventuellement d'autres objets, appelez tout simplement la fonction `dumps()`. La fonction `loads()`, quant à elle, désérialise un flux de données. Pour un contrôle plus fin des opérations de sérialisation ou désérialisation, créez un objet `Pickler` ou `Unpickler`.

Le module `pickle` définit les constantes suivantes :

`pickle.``HIGHEST_PROTOCOL`

  Entier qui donne la version du protocole le plus récent qui soit disponible. Ce nombre peut être passé comme paramètre protocol aux fonctions `dump()` et `dumps()` ainsi qu'au constructeur de la classe `Pickler`.

`pickle.``DEFAULT_PROTOCOL`

  Entier qui donne la version du protocole employé par défaut pour la sérialisation. Il peut être moindre que `HIGHEST_PROTOCOL`. La valeur actuelle est 4, sachant que le protocole correspondant a été introduit en Python 3.4 et n'est pas compatible avec les versions antérieures.

  Modifié dans la version 3.0: Le protocole par défaut est devenu le protocole 3.

  Modifié dans la version 3.8: Le protocole par défaut est devenu le protocole 4.

Le module `pickle` contient quelques fonctions pour faciliter la sérialisation.

`pickle.``dump`(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)

  Écrit la représentation sérialisée de l'objet obj dans l'objet fichier-compatible file, qui doit être ouvert. Ceci est l'équivalent de `Pickle(file, protocol).dump(obj)`.

  Les arguments file, protocol, fix_imports et buffer_callback sont identiques à ceux du constructeur de la classe `Pickler`.

  Modifié dans la version 3.8: ajout de l'argument buffer_callback.

`pickle.``dumps`(obj, protocol=None, *, fix_imports=True, buffer_callback=None)

  Renvoie la représentation sérialisée de obj sous forme de `bytes`, au lieu de l'écrire dans un fichier.

  Les arguments protocol, fix_imports et buffer_callback sont identiques à ceux du constructeur de la classe `Pickler`.

  Modifié dans la version 3.8: ajout de l'argument buffer_callback.

`pickle.``load`(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

  Charge la représentation sérialisée d'un objet depuis l'objet fichier-compatible ouvert file, et renvoie l'objet reconstitué obtenu. Ceci est l'équivalent de `Unpickler(file).load()`.

  La version du protocole utilisée pour la sérialisation est détectée automatiquement, d'où l'absence d'un argument. Les octets situés après la représentation sérialisée de l'objet sont ignorés.

  Les arguments file, fix_imports, encoding, errors, strict et buffers sont identiques à ceux du constructeur de la classe `Unpickler`.

  Modifié dans la version 3.8: Ajout de l'argument buffers.

`pickle.``loads`(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

  Renvoie l'objet reconstitué à partir de la représentation sérialisée data, qui doit être fournie sous la forme d'un bytes-like object.

  La version du protocole utilisée pour la sérialisation est détectée automatiquement, d'où l'absence d'un argument. Les octets situés après la représentation sérialisée de l'objet sont ignorés.

  Arguments fix_imports, encoding, errors, strict and buffers have the same meaning as in the `Unpickler` constructor.

  Modifié dans la version 3.8: Ajout de l'argument buffers.

Le module `pickle` définit trois types d'exceptions :

exception `pickle.``PickleError`

  Classe mère commune aux autres exceptions de sérialisation. Elle hérite de `Exception`.

exception `pickle.``PicklingError`

  Exception levée lorsqu'un objet impossible à sérialiser est rencontré par un `Pickler`. Elle hérite de `PickleError`.

  Lisez Quels objets sont sérialisables ? pour en savoir plus sur les types d'objets qui peuvent être sérialisés.

exception `pickle.``UnpicklingError`

  Exception levée lorsqu'un flux binaire ne peut pas être désérialisé, par exemple s'il est corrompu ou en cas de violation de la sécurité. Elle hérite de `PickleError`.

  Veuillez noter que d'autres exceptions peuvent être levées durant la désérialisation, comme `AttributeError`, `EOFError`, `ImportError` et `IndexError` (liste non-exhaustive).

Le module `pickle` exporte trois classes : `Pickler`, `Unpickler` et `PickleBuffer`.

class `pickle.``Pickler`(file, protocol=None, *, fix_imports=True, buffer_callback=None)

  Classe d'objets qui implémentent la sérialisation vers un flux binaire.

  L'argument optionnel protocol détermine la version du protocole de sérialisation à employer, entre 0 et `HIGHEST_PROTOCOL`. La valeur par défaut est celle de `DEFAULT_PROTOCOL`. Avec un nombre strictement négatif, c'est `HIGHEST_PROTOCOL` qui est utilisé.

  L'argument file peut être un fichier sur disque ouvert en mode binaire pour l'écriture, une instance de la classe `io.BytesIO`, ou plus généralement un objet quelconque qui possède une méthode `write()` acceptant d'être appelée sur un argument unique de type `bytes`.

  Si fix_imports est vrai et protocol est inférieur ou égal à 2, les noms des modules sont reliés par pickle aux anciens noms qui avaient cours en Python 2, afin que le flux sérialisé soit lisible aussi bien par Python 2 que Python 3.

  Si buffer_callback vaut `None` (comme par défaut), les vues de tampon sont sérialisées dans file avec le reste du flux.

  Dans le cas où le buffer_callback n'est pas `None`, il doit pouvoir être appelé un nombre quelconque de fois avec une vue d'un tampon. S'il renvoie une valeur évaluée comme fausse (telle que `None`), le tampon est considéré en marge de la sérialisation (ou « hors-bande »), sinon il est sérialisé dans le flux binaire.

  Une erreur se produit si buffer_callback vaut autre chose que `None` et protocol est 4 ou inférieur, ou `None`.

  Modifié dans la version 3.8: ajout de l'argument buffer_callback.

  `dump`(obj)

    Écrit la représentation sérialisée de l'objet obj dans le fichier ouvert passé au constructeur.

  `persistent_id`(obj)

    Ne fait rien par défaut. Cette méthode est destinée à être implémentée par une classe fille.

    Si `persistent_id()` renvoie `None`, obj est sérialisé normalement. Toute autre valeur est reprise par le `Pickler` comme ID persistant pour obj. Le sens de cet ID persistant doit être défini par `Unpickler.persistent_load()`. Veuillez noter que la valeur renvoyée par `persistent_id()` ne peut pas porter elle-même d'ID persistant.

    La section Persistance d'objets externes donne des détails et exemples.

  `dispatch_table`

    La table de distribution d'un sérialiseur est un tableau associatif dont les clés sont des classes et les valeurs, des fonctions de réduction. La manière la plus directe de déclarer une fonction de réduction est la fonction `copyreg.pickle()`. Une fonction de réduction prend un unique argument, qui doit être de la classe en question, et obéit à la même interface qu'une méthode `__reduce__()`.

    Lorsqu'un sérialiseur ne possède pas l'attribut `dispatch_table`, comme c'est le cas par défaut, il utilise le tableau global du module `copyreg`. Afin de personnaliser l'opération de sérialisation pour un sérialiseur particulier, on peut affecter à son attribut `dispatch_table` un objet compatible avec les dictionnaires. Une autre possibilité est de définir l'attribut `dispatch_table` dans une classe fille de `Pickler`. Sa valeur sera alors utilisée pour toutes les instances de cette classe fille.

    Voir Tables de distribution pour des exemples d'utilisation.

    Nouveau dans la version 3.3.

  `reducer_override`(obj)

    Réducteur spécial, qui peut se définir dans une classe fille de `Pickler`. Cette méthode doit se conformer à l'interface des méthodes `__reduce__()`. Elle prend en général le pas sur les réducteurs contenus dans l'attribut `dispatch_table`. Cependant, elle peut choisir de renvoyer `NotImplemented` pour que `dispatch_table` soit utilisé à la place.

    Voir Réduction personnalisée pour les types, fonctions et autres objets pour un exemple détaillé.

    Nouveau dans la version 3.8.

  `fast`

    Cet attribut est obsolète. Une valeur vraie (« mode rapide ») désactive la mémorisation des objets au fur et à mesure de leur sérialisation, qui permet habituellement de représenter les doublons par une unique référence. Cette option accélère la sérialisation en évitant des opcodes PUT superflus. Elle ne doit pas être utilisée sur un objet contenant une référence à lui-même, car le `Pickler` entre alors dans une récursion infinie.

    Utilisez plutôt `pickletools.optimize()` pour obtenir des données sérialisées plus compactes.

class `pickle.``Unpickler`(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

  Les objets de cette classe sont des désérialiseurs, qui lisent un flux de données pour le convertir en objet.

  Il n'y a nul besoin d'argument protocol. La version du protocole avec lequel sont encodées les données est déterminée automatiquement.

  L'argument file doit posséder trois méthodes qui proviennent de l'interface de `io.BufferedIOBase`. Ce sont : `read()`, prenant un entier, `readinto()`, prenant un tampon, et `readline()`, sans arguments. file peut donc être aussi bien un fichier sur disque ouvert en mode lecture binaire qu'un objet `io.BytesIO`, ou un objet quelconque vérifiant ces critères.

  Les paramètres facultatifs fix_imports, encoding et errors sont dédiés à la compatibilité de la désérialisation avec les flux binaires générés par Python 2. Si fix_imports est vrai, pickle tente de modifier les anciens noms des modules que l'on trouve en Python 2, pour les remplacer par ceux en usage en Python 3. Les paramètres encoding et errors contrôlent la façon de décoder les chaînes de caractères 8 bits. Leurs valeurs par défaut respectives sont `'ASCII'` et `'strict'`. encoding peut être mis à `'bytes'` pour lire des chaînes d'octets en tant que bytes. Il doit être mis à `'latin1'` pour désérialiser des tableaux NumPy ou des instances de `datetime`, `date` et `time` sérialisées par Python 2.

  Si buffers vaut None (comme par défaut), toutes les données nécessaires à la désérialisation doivent être contenues dans le flux binaire. Ceci signifie que l'argument buffer_callback valait None lors de la construction du `Pickler` (ou dans l'appel à `dump()` ou `dumps()`).

  If buffers is not None, it should be an iterable of buffer-enabled objects that is consumed each time the pickle stream references an out-of-band buffer view. Such buffers have been given in order to the buffer_callback of a Pickler object.

  Modifié dans la version 3.8: Ajout de l'argument buffers.

  `load`()

    Lit la représentation sérialisée d'un objet depuis le fichier ouvert passé au constructeur, et reconstitue l'objet qui y est stocké, avec tous les objets qu'il contient. Les octets situés au-delà de la fin de la représentation binaire sont ignorés.

  `persistent_load`(pid)

    Par défaut, cette méthode lève une exception `UnpicklingError`.

    Si elle est définie autrement dans une sous-classe, `persistent_load()` doit renvoyer l'objet correspondant à l'ID persistant pid. Si celui-ci est invalide, elle doit lever une exception `UnpicklingError`.

    La section Persistance d'objets externes donne des détails et exemples.

  `find_class`(module, name)

    Importe module si besoin, et renvoie l'objet du nom name qu'il contient. module et name sont des chaînes de caractères (classe `str`). Contrairement à ce que son nom laisse penser, `find_class()` est également appelée pour trouver les fonctions.

    Les classes filles peuvent redéfinir cette méthode pour restreindre la désérialisation à certains types d'objets ou à d'autres conditions, notamment en vue de réduire les risques de sécurité. Voir Restriction des noms dans l'espace de nommage global pour plus de détails.

    Lève un événement d'audit `pickle.find_class` avec les arguments `module` et `name`.

class `pickle.``PickleBuffer`(buffer)

  Encapsule un objet tampon contenant des données sérialisables. buffer doit être un objet prenant en charge le protocole tampon, comme un objet octet-compatible ou un tableau n-dimensionnel.

  Les objets `PickleBuffer` savent gérer le protocole tampon. Il est donc possible de les passer à d'autres API qui attendent un tampon, comme `memoryview`.

  Les objets `PickleBuffer` ne peuvent être sérialisés qu'avec le protocole 5 ou supérieur. Ils sont susceptibles d'être sérialisés hors-bande.

  Nouveau dans la version 3.8.

  `raw`()

    Renvoie une `memoryview` de l'espace mémoire sous-jacent à ce tampon. La memoryview renvoyée est unidimensionnelle et C-contiguë. Elle a le format `B` (octets sans signe). `BufferError` est levée si le tampon n'est ni C-contigu, ni Fortran-contigu.

  `release`()

    Release the underlying buffer exposed by the PickleBuffer object.

## Quels objets sont sérialisables ?

Les objets des types suivants peuvent être sérialisés :

- `None`, `True`, and `False`;

- integers, floating-point numbers, complex numbers;

- strings, bytes, bytearrays;

- tuples, lists, sets, and dictionaries containing only picklable objects;

- functions (built-in and user-defined) accessible from the top level of a module (using `def`, not `lambda`);

- classes accessible from the top level of a module;

- les instances de telles classes, à condition que leur `__dict__`, ou la valeur de retour de `__getstate__()`, soit sérialisable (voir Sérialisation des instances d'une classe pour plus d'informations).

Si vous essayez de sérialiser un objet qui ne peut pas l'être, une exception de type `PicklingError` est levée. Lorsque cela se produit, il est possible qu'un certain nombre d'octets aient déjà été écrits dans le fichier ou flux. La sérialisation d'une structure de donnée avec de nombreux niveaux d'imbrication peut lever une exception `RecursionError`. Pour augmenter la limite (avec précaution), voir `sys.setrecursionlimit()`.

Note that functions (built-in and user-defined) are pickled by fully qualified name, not by value. 2 This means that only the function name is pickled, along with the name of the containing module and classes. Neither the function's code, nor any of its function attributes are pickled. Thus the defining module must be importable in the unpickling environment, and the module must contain the named object, otherwise an exception will be raised. 3

Similarly, classes are pickled by fully qualified name, so the same restrictions in the unpickling environment apply. Note that none of the class's code or data is pickled, so in the following example the class attribute `attr` is not restored in the unpickling environment:

    class Foo:
        attr = 'A class attribute'

    picklestring = pickle.dumps(Foo)

These restrictions are why picklable functions and classes must be defined at the top level of a module.

De même, lorsque les instances d'une certaine classe sont sérialisées, seuls les attributs de l'instance sont inclus, mais pas le code de leur classe ni les données qu'elle pourrait contenir. Ceci est intentionnel : vous pouvez corriger des bogues dans une classe ou ajouter des méthodes, et désérialiser malgré tout des objets instanciés avec une version plus ancienne de la classe. Si vous stockez des objets destinés être conservés pendant longtemps, et que leur classe est susceptible de connaître de nombreuses évolutions, il peut s'avérer utile d'associer aux objets un numéro de version afin que des conversions puissent être implémentées dans la méthode `__setstate__()` de la classe.

## Sérialisation des instances d'une classe

Dans cette section sont décrits les mécanismes généraux qui s'offrent à vous pour définir, personnaliser et contrôler la manière dont les instances d'une classe sont sérialisées et désérialisées.

Dans la plupart des cas, il n'y a besoin de rien pour que les instances d'une classe puissent être sérialisées. Par défaut, pickle accède à la classe et aux attributs de l'instance par introspection. Lorsqu'une instance est désérialisée, sa méthode `__init__()` n'est normalement pas appelée. Une nouvelle instance est créée sans être initialisée, et ses attributs sont simplement restaurés à partir des valeurs conservées. En d'autres termes, les opérations sont celles qu'effectue le code suivant :

    def save(obj):
        return (obj.__class__, obj.__dict__)

    def restore(cls, attributes):
        obj = cls.__new__(cls)
        obj.__dict__.update(attributes)
        return obj

Les classes peuvent personnaliser le comportement par défaut en définissant des méthodes spéciales :

`object.``__getnewargs_ex__`()

  Dans les protocoles 2 et suivants, les classes peuvent personnaliser les valeurs passées à la méthode `__new__()` lors de la désérialisation. Elles le font en définissant une méthode `__getnewargs_ex__()` qui renvoie un couple `(args, kwargs)`, où args est un n-uplet des arguments positionnels et kwargs un dictionnaire des arguments nommés qui seront passés à `__new__()` — autrement dit, l'appel sera `classe.__new__(*args, **kwargs)`.

  Définissez cette méthode seulement si la méthode `__new__()` de votre classe demande des arguments nommés. Dans le cas contraire, mieux vaut définir `__getnewargs__()` pour préserver la compatibilité avec les protocoles anciens.

  Modifié dans la version 3.6: `__getnewargs_ex__()` est désormais appelée dans les protocoles 2 et 3.

`object.``__getnewargs__`()

  Comme `__getnewargs_ex__()`, mais ne permet que les arguments positionnels. Cette méthode doit renvoyer le n-uplet `args` des arguments passés à `__new__()` lors de la désérialisation : l'appel sera `classe.__new__(*args)`.

  Si `__getnewargs_ex__()` est définie, elle prend la priorité et `__getnewargs__()` n'est jamais appelée.

  Modifié dans la version 3.6: Auparavant, `__getnewargs__()` était appelée au lieu de `__getnewargs_ex__()` dans les protocoles 2 et 3.

`object.``__getstate__`()

  Des personnalisations plus poussées de la sérialisation sont possibles à l'aide de la méthode `__getstate__()`. Lorsque `__getstate__()` est définie, l'objet qu'elle renvoie (état de l'instance) est sérialisé en lieu et place du `__dict__`, le dictionnaire des attributs de l'instance.

`object.``__setstate__`(state)

  Lors de la désérialisation, l'état de l'instance est passé à la méthode `__setstate__()`, si elle est définie (l'objet state n'a pas besoin d'être un dictionnaire). Si elle ne l'est pas, les attributs de l'objet sont tirés de l'état, qui dans ce cas doit être obligatoirement un dictionnaire.

  Note

  Si `__getstate__()` renvoie une valeur fausse, `__setstate__()` ne sera pas appelée à la désérialisation.

Voir Traitement des objets à état pour plus d'informations sur `__getstate__()` et `__setstate__()`.

Note

Lors de la désérialisation, des méthodes comme `__getattr__()`, `__getattribute__()` et `__setattr__()` sont susceptibles d'être appelées sur l'instance. Si ces méthodes reposent sur des invariants internes à l'objet, leur classe doit les initialiser dans la méthode `__new__()`, puisque la méthode `__init__()` n'est pas appelée.

Comme nous le verrons, pickle ne fait pas directement appel aux méthodes ci-dessus. En réalité, elles font partie du protocole de copie, qui implémente la méthode spéciale `__reduce__()`. Ce protocole constitue une interface unifiée pour l'accès aux données nécessaires à la sérialisation comme à la copie 4.

Bien que la méthode `__reduce__()` ouvre davantage de possibilités, elle conduit plus facilement à des erreurs. C'est pourquoi les auteurs de classes sont encouragés à utiliser lorsque c'est possible l'interface de plus haut niveau avec `__getnewargs_ex__()`, `__getstate__()` et `__state__()`. Cependant, il existe des cas où l'on ne peut pas se passer de `__reduce__()`, ou bien elle permet une sérialisation plus efficace.

`object.``__reduce__`()

  Voici l'interface de la méthode `__reduce__()`. Elle ne prend aucun argument et renvoie soit une chaîne de caractères, soit (c'est conseillé) un n-uplet. On appelle souvent l'objet renvoyé « valeur de réduction ».

  If a string is returned, the string should be interpreted as the name of a global variable. It should be the object's local name relative to its module; the pickle module searches the module namespace to determine the object's module. This behaviour is typically useful for singletons.

  Si c'est un n-uplet qui est renvoyé, ses éléments sont interprétés dans l'ordre comme suit. Les deux premiers éléments sont obligatoires, les quatre suivants sont facultatifs et peuvent être simplement omis, ou bien mis à `None`. Les éléments sont, dans l'ordre :

  - Un objet appelable qui sera appelé pour créer l'objet initial.

  - Un n-uplet d'arguments passés à cet objet appelable. Donnez un n-uplet vide si l'objet appelable n'accepte pas d'arguments.

  - L'état de l'objet, qui sera passé à la méthode `__setstate__()` comme vu précédemment. Si la méthode n'existe pas, cet élément doit être un dictionnaire, et ses éléments compléteront l'attribut `__dict__`.

  - Un itérateur (non pas une séquence). Les éléments qu'il fournit sont ajoutés à l'objet un par un avec la méthode `append()`, ou bien plusieurs à la fois avec la méthode `extend()`. Ceci est principalement utile aux les classes héritant de `list`, mais peut aussi servir sur d'autres classes, la seule contrainte étant qu'elles implémentent `append()` et `extend()` avec les bonnes signatures (l'une ou l'autre de ces méthodes est utilisée selon la version du protocole pickle et le nombre d'éléments à ajouter, c'est pourquoi elles doivent être définies toutes les deux).

  - Un itérateur (non pas une séquence). Les éléments qu'il fournit doivent être des couples `(clé, valeur)`. Ils sont ajoutés dans l'objet par affectation aux clés : `objet[clé] = valeur`. Ceci est principalement utile aux classes héritant de `dict`, mais peut servir à d'autres classes à la seule condition qu'elles implémentent la méthode `__setitem__()`.

  - Un objet appelable qui puisse recevoir en arguments l'objet et son état. Ceci permet de redéfinir le processus de reconstruction des attributs pour un objet en particulier, outrepassant la méthode `__setstate__()`. Si cet objet appelable est fourni, `__setstate__()` n'est pas appelée.

    Nouveau dans la version 3.8: ajout du sixième élément.

`object.``__reduce_ex__`(protocol)

  Il est également possible de définir une méthode `__reduce_ex__()`. La seule différence est qu'elle prend la version du protocole en argument. Si elle est définie, elle prend le pas sur `__reduce__()`. De plus, `__reduce__()` devient automatiquement un alias pour sa version étendue. Cette méthode est principalement destinée à renvoyer des valeurs de réduction compatibles avec les versions anciennes de Python.

### Persistance d'objets externes

Pour les besoins de la persistance, `pickle` permet des références à des objets en dehors du flux sérialisé. Ils sont identifiés par un ID persistant. Le protocole 0 requiert que cet ID soit une chaîne de caractères alphanumériques 5. Les suivants autorisent un objet quelconque.

`pickle` délègue la résolution des ID à des méthodes définies par l'utilisateur sur les objets sérialiseurs et désérialiseurs, à savoir `persistent_id()` et `persistent_load()`.

Pour affecter à des objets leurs ID persistants provenant d'une source externe, le sérialiseur doit posséder une méthode `persistent_id()` qui prend un objet et renvoie soit `None`, soit son ID. Si cette méthode renvoie `None`, l'objet est sérialisé de la manière habituelle. Si un ID est renvoyé, sous forme de chaîne de caractères, c'est cette chaîne qui est sérialisée et elle est marquée de manière spéciale pour être reconnue comme un ID persistant.

Pour désérialiser des objets identifiés par un ID externe, un désérialiseur doit posséder une méthode `persistent_load()` qui prend un ID et renvoie l'objet qu'il désigne.

Voici un exemple complet qui montre comment sérialiser des objets externes en leur affectant des ID persistants.

    # Simple example presenting how persistent ID can be used to pickle
    # external objects by reference.

    import pickle
    import sqlite3
    from collections import namedtuple

    # Simple class representing a record in our database.
    MemoRecord = namedtuple("MemoRecord", "key, task")

    class DBPickler(pickle.Pickler):

        def persistent_id(self, obj):
            # Instead of pickling MemoRecord as a regular class instance, we emit a
            # persistent ID.
            if isinstance(obj, MemoRecord):
                # Here, our persistent ID is simply a tuple, containing a tag and a
                # key, which refers to a specific record in the database.
                return ("MemoRecord", obj.key)
            else:
                # If obj does not have a persistent ID, return None. This means obj
                # needs to be pickled as usual.
                return None

    class DBUnpickler(pickle.Unpickler):

        def __init__(self, file, connection):
            super().__init__(file)
            self.connection = connection

        def persistent_load(self, pid):
            # This method is invoked whenever a persistent ID is encountered.
            # Here, pid is the tuple returned by DBPickler.
            cursor = self.connection.cursor()
            type_tag, key_id = pid
            if type_tag == "MemoRecord":
                # Fetch the referenced record from the database and return it.
                cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
                key, task = cursor.fetchone()
                return MemoRecord(key, task)
            else:
                # Always raises an error if you cannot return the correct object.
                # Otherwise, the unpickler will think None is the object referenced
                # by the persistent ID.
                raise pickle.UnpicklingError("unsupported persistent object")

    def main():
        import io
        import pprint

        # Initialize and populate our database.
        conn = sqlite3.connect(":memory:")
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
        tasks = (
            'give food to fish',
            'prepare group meeting',
            'fight with a zebra',
            )
        for task in tasks:
            cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))

        # Fetch the records to be pickled.
        cursor.execute("SELECT * FROM memos")
        memos = [MemoRecord(key, task) for key, task in cursor]
        # Save the records using our custom DBPickler.
        file = io.BytesIO()
        DBPickler(file).dump(memos)

        print("Pickled records:")
        pprint.pprint(memos)

        # Update a record, just for good measure.
        cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")

        # Load the records from the pickle data stream.
        file.seek(0)
        memos = DBUnpickler(file, conn).load()

        print("Unpickled records:")
        pprint.pprint(memos)

    if __name__ == '__main__':
        main()

### Tables de distribution

Pour personnaliser la sérialisation d'une classe à un endroit particulier sans affecter le reste du code, on peut créer un sérialiseur avec une table de distribution spécifique.

La table de distribution gérée par le module `copyreg` est disponible sous le nom `copyreg.dispatch_table`. On peut donc utiliser une copie modifiée de `copyreg.dispatch_table` comme table spécifique à un sérialiseur.

Par exemple, le code :

    f = io.BytesIO()
    p = pickle.Pickler(f)
    p.dispatch_table = copyreg.dispatch_table.copy()
    p.dispatch_table[SomeClass] = reduce_SomeClass

crée une instance de la classe `pickle.Pickler` avec une table de distribution propre qui traite la classe `SomeClass` de manière spécifique. Le code :

    class MyPickler(pickle.Pickler):
        dispatch_table = copyreg.dispatch_table.copy()
        dispatch_table[SomeClass] = reduce_SomeClass
    f = io.BytesIO()
    p = MyPickler(f)

does the same but all instances of `MyPickler` will by default share the private dispatch table. On the other hand, the code

    copyreg.pickle(SomeClass, reduce_SomeClass)
    f = io.BytesIO()
    p = pickle.Pickler(f)

modifies the global dispatch table shared by all users of the `copyreg` module.

### Traitement des objets à état

L'exemple suivant illustre comment modifier la sérialisation pour une classe. La classe `TextReader` ouvre un fichier de texte, et sa méthode `readline()` renvoie le numéro de la ligne suivante et son contenu chaque fois qu'elle est appelée. Si une instance de `TextReader` est sérialisée, tous les attributs sauf le fichier ouvert sont enregistrés. Lorsque l'instance est désérialisée, le fichier est rouvert et la lecture reprend là où elle s'était arrêtée. Ceci est implémenté à travers les méthodes `__setstate__()` et `__getstate__()`.

    class TextReader:
        """Print and number lines in a text file."""

        def __init__(self, filename):
            self.filename = filename
            self.file = open(filename)
            self.lineno = 0

        def readline(self):
            self.lineno += 1
            line = self.file.readline()
            if not line:
                return None
            if line.endswith('\n'):
                line = line[:-1]
            return "%i: %s" % (self.lineno, line)

        def __getstate__(self):
            # Copy the object's state from self.__dict__ which contains
            # all our instance attributes. Always use the dict.copy()
            # method to avoid modifying the original state.
            state = self.__dict__.copy()
            # Remove the unpicklable entries.
            del state['file']
            return state

        def __setstate__(self, state):
            # Restore instance attributes (i.e., filename and lineno).
            self.__dict__.update(state)
            # Restore the previously opened file's state. To do so, we need to
            # reopen it and read from it until the line count is restored.
            file = open(self.filename)
            for _ in range(self.lineno):
                file.readline()
            # Finally, save the file.
            self.file = file

Voici un exemple d'utilisation :

    >>> reader = TextReader("hello.txt")
    >>> reader.readline()
    '1: Hello world!'
    >>> reader.readline()
    '2: I am line number two.'
    >>> new_reader = pickle.loads(pickle.dumps(reader))
    >>> new_reader.readline()
    '3: Goodbye!'

## Réduction personnalisée pour les types, fonctions et autres objets

Nouveau dans la version 3.8.

Parfois, la simple utilisation de `dispatch_table` n'offre pas assez de flexibilité. On peut vouloir changer la méthode de sérialisation selon d'autres critères que le type de l'objet, ou bien personnaliser la sérialisation des fonctions et des classes.

Dans ces cas, il est possible d'écrire une méthode `reducer_override()` dans une classe fille de `Pickler`. Cette méthode renvoie un n-uplet de réduction arbitraire (voir `__reduce__()`). Elle peut aussi renvoyer `NotImplemented`, auquel cas la méthode habituelle de réduction par table s'applique.

Si `dispatch_table` et `reducer_override()` sont tous les deux définis, `reducer_override()` a la priorité.

Note

Pour des raisons de performance, la méthode `reducer_override()` n'est jamais appelée sur `None`, `True`, `False`, ainsi que les instances exactes (pas dérivées) de `int`, `float`, `bytes`, `str`, `dict`, `set`, `frozenset`, `list` et `tuple`.

Voici un exemple simple qui implémente la sérialisation d'une classe :

    import io
    import pickle

    class MyClass:
        my_attribute = 1

    class MyPickler(pickle.Pickler):
        def reducer_override(self, obj):
            """Custom reducer for MyClass."""
            if getattr(obj, "__name__", None) == "MyClass":
                return type, (obj.__name__, obj.__bases__,
                              {'my_attribute': obj.my_attribute})
            else:
                # For any other object, fallback to usual reduction
                return NotImplemented

    f = io.BytesIO()
    p = MyPickler(f)
    p.dump(MyClass)

    del MyClass

    unpickled_class = pickle.loads(f.getvalue())

    assert isinstance(unpickled_class, type)
    assert unpickled_class.__name__ == "MyClass"
    assert unpickled_class.my_attribute == 1

## Tampons hors-bande

Nouveau dans la version 3.8.

Le module `pickle` est parfois utilisé pour transférer des quantités énormes de données. Il peut devenir important de réduire les copies de mémoire au minimum pour préserver la performance et diminuer l'usage des ressources matérielles. Cependant, dans son contexte courant d'utilisation, le module `pickle` effectue des copies depuis et vers le flux de données pour les besoins de la conversion de structures d'objets semblables à des graphes en flux séquentiels d'octets.

Cette contrainte peut être levée si le producteur (qui implémente les types d'objets à transférer) et le consommateur (qui implémente le système de communication) emploient les possibilités de transfert hors-bande offertes par les protocoles 5 et suivants.

### API des producteurs

Les objets de grande taille à sérialiser doivent posséder une méthode `__reduce_ex__()` qui, lorsqu'elle est appelée pour le protocole 5 ou plus, renvoie un objet `PickleBuffer` au lieu d'un objet `bytes` dès que la taille le justifie.

Les objets `PickleBuffer` ne font que signaler que leur tampon permet le transfert hors-bande. Ils demeurent compatibles avec l'utilisation classique du module `pickle`. Cependant, les consommateurs peuvent aussi choisir d'indiquer à `pickle` qu'ils gèrent eux-mêmes ces tampons.

### API des consommateurs

Un système de communication peut gérer de manière spécifique les objets `PickleBuffer` générés lors de la sérialisation d'un réseau d'objets.

Du côté de l'expéditeur, il faut passer le paramètre buffer_callback à `Pickler` (ou à `dump()` ou `dumps()`). Le buffer_callback sera appelé avec chaque `PickleBuffer` généré lors de la sérialisation du réseau d'objets. Les tampons accumulés par le buffer_callback ne verront pas leurs données copiées dans le flux sérialisé. Il leur sera substitué un marqueur léger.

Du côté du receveur, il faut passer l'argument buffers à `Unpickler` (ou `load()` ou bien `loads()`). buffers est un itérable des tampons passés à buffer_callback. Il doit fournir les tampons dans le même ordre que celui dans lequel ils ont été passés à buffer_callback. Les tampons fournis constituent la source des données qu'attendent les reconstructeurs des objets dont la sérialisation a abouti aux objets `PickleBuffer`.

Entre expéditeur et receveur, le système de communication peut implémenter son propre mécanisme de transfert pour les tampons hors-bande. Parmi les optimisations possibles se trouvent l'utilisation de mémoire partagée et la compression spécifique au type de données.

### Exemple

Voici un exemple trivial où est implémentée une classe fille de `bytearray` capable de sérialisation hors-bande.

    class ZeroCopyByteArray(bytearray):

        def __reduce_ex__(self, protocol):
            if protocol >= 5:
                return type(self)._reconstruct, (PickleBuffer(self),), None
            else:
                # PickleBuffer is forbidden with pickle protocols <= 4.
                return type(self)._reconstruct, (bytearray(self),)

        @classmethod
        def _reconstruct(cls, obj):
            with memoryview(obj) as m:
                # Get a handle over the original buffer object
                obj = m.obj
                if type(obj) is cls:
                    # Original buffer object is a ZeroCopyByteArray, return it
                    # as-is.
                    return obj
                else:
                    return cls(obj)

Lorsqu'il rencontre le bon type, le reconstructeur (la méthode de classe `_reconstruct`) renvoie directement le tampon original. Il s'agit d'une manière simple de simuler l'absence de copie dans cet exemple simpliste.

En tant que consommateur des objets, on peut les sérialiser de la manière classique. La désérialisation conduit alors à une copie.

    b = ZeroCopyByteArray(b"abc")
    data = pickle.dumps(b, protocol=5)
    new_b = pickle.loads(data)
    print(b == new_b)  # True
    print(b is new_b)  # False: a copy was made

Mais en passant un buffer_callback et en donnant les tampons accumulés au désérialiseur, il n'y a plus de copie.

    b = ZeroCopyByteArray(b"abc")
    buffers = []
    data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)
    new_b = pickle.loads(data, buffers=buffers)
    print(b == new_b)  # True
    print(b is new_b)  # True: no copy was made

Cet exemple est limité par le fait que `bytearray` effectue sa propre allocation de mémoire. Il n'est pas possible de créer un `bytearray` sur la mémoire d'un autre objet. Cependant, certains types de données que l'on trouve dans des bibliothèques externes, comme les tableaux NumPy, n'ont pas cette limitation. Le passage hors-bande permet alors de n'effectuer aucune copie (ou bien de minimiser le nombre de copies) lors du transfert de données d'un système à l'autre ou d'un processus à l'autre.

Voir aussi

PEP 574 — Protocole pickle 5 avec données hors-bande

## Restriction des noms dans l'espace de nommage global

Par défaut, la désérialisation importe toutes les classes ou fonctions que demande le flux de données. Dans bien des cas, ce comportement est inacceptable, puisqu'il permet de faire exécuter du code arbitraire dans l'environnement de désérialisation. Observez le résultat de ce flux de données fait-main lorsqu'il est lu :

    >>> import pickle
    >>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
    hello world
    0

Dans cet exemple, le désérialiseur importe la fonction `os.system()` et l'applique à la chaîne de caractères `"echo hello world"`. C'est inoffensif, mais il n'est pas difficile d'imaginer des variantes qui endommageraient le système.

C'est pour cette raison qu'il s'avère parfois nécessaire de contrôler ce qui peut être désérialisé. Cela est possible en redéfinissant la méthode `Unpickler.find_class()`. Contrairement à ce que son nom laisse penser, `Unpickler.find_class()` est appelée pour tous les noms à chercher dans l'espace de nommage global, ce qui inclut les classes mais aussi les fonctions. Par ce biais, il est possible d'interdire complètement la résolution des noms globaux ou de la restreindre à un sous-ensemble que l'on considère sûr.

Voici un exemple de désérialiseur qui permet seulement la désérialisation d'un petit nombre de classes sûres du module `builtins`.

    import builtins
    import io
    import pickle

    safe_builtins = {
        'range',
        'complex',
        'set',
        'frozenset',
        'slice',
    }

    class RestrictedUnpickler(pickle.Unpickler):

        def find_class(self, module, name):
            # Only allow safe classes from builtins.
            if module == "builtins" and name in safe_builtins:
                return getattr(builtins, name)
            # Forbid everything else.
            raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
                                         (module, name))

    def restricted_loads(s):
        """Helper function analogous to pickle.loads()."""
        return RestrictedUnpickler(io.BytesIO(s)).load()

A sample usage of our unpickler working as intended:

    >>> restricted_loads(pickle.dumps([1, 2, range(15)]))
    [1, 2, range(0, 15)]
    >>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
    Traceback (most recent call last):
      ...
    pickle.UnpicklingError: global 'os.system' is forbidden
    >>> restricted_loads(b'cbuiltins\neval\n'
    ...                  b'(S\'getattr(__import__("os"), "system")'
    ...                  b'("echo hello world")\'\ntR.')
    Traceback (most recent call last):
      ...
    pickle.UnpicklingError: global 'builtins.eval' is forbidden

Comme le montre l'exemple, il faut faire attention aux objets que l'on autorise à être désérialisés. Si la sécurité est une priorité, il peut être sage de se tourner vers des alternatives comme l'API du module `xmlrpc.client`, ou des bibliothèques tierces.

## Performances

Recent versions of the pickle protocol (from protocol 2 and upwards) feature efficient binary encodings for several common features and built-in types. Also, the `pickle` module has a transparent optimizer written in C.

## Exemples

Dans les cas les plus simples, utilisez les fonctions `dump()` et `load()`.

    import pickle

    # An arbitrary collection of objects supported by pickle.
    data = {
        'a': [1, 2.0, 3+4j],
        'b': ("character string", b"byte string"),
        'c': {None, True, False}
    }

    with open('data.pickle', 'wb') as f:
        # Pickle the 'data' dictionary using the highest protocol available.
        pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

Le code suivant lit les données qui viennent d'être sérialisées :

    import pickle

    with open('data.pickle', 'rb') as f:
        # The protocol version used is detected automatically, so we do not
        # have to specify it.
        data = pickle.load(f)

Voir aussi

Module `copyreg`

  Enregistre les fonctions de sérialisation pour les types définis par l'utilisateur.

Module `pickletools`

  Outils pour travailler sur les données sérialisées et les analyser.

Module `shelve`

  Bases de données indexées (module fondé sur `pickle`).

Module `copy`

  Copie superficielle ou récursive d'objets.

Module `marshal`

  Sérialisation haute-performance des types natifs.

Notes

1

  À ne pas confondre avec ce que fait le module `marshal`.

2

  C'est la raison pour laquelle les fonctions `lambda` ne peuvent pas être sérialisées : elles partagent toutes le même nom, à savoir `<lambda>`.

3

  L'exception levée est généralement de type `ImportError` ou `AttributeError`, mais ce n'est pas systématique.

4

  Le module `copy` fait appel à ce protocole pour les opérations de copie superficielle comme récursive.

5

  The limitation on alphanumeric characters is due to the fact that persistent IDs in protocol 0 are delimited by the newline character. Therefore if any kind of newline characters occurs in persistent IDs, the resulting pickled data will become unreadable.