"pickle" --- Python object serialization
****************************************

**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 mutables.

* "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

   Common base class for the other pickling exceptions.  It inherits
   from "Exception".

exception pickle.PicklingError

   Error raised when an unpicklable object is encountered by
   "Pickler". It inherits from "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

   Error raised when there is a problem unpickling an object, such as
   a data corruption or a security violation.  It inherits from
   "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.

   If *buffer_callback* is "None" (the default), buffer views are
   serialized into *file* as part of the pickle stream.

   If *buffer_callback* is not "None", then it can be called any
   number of times with a buffer view.  If the callback returns a
   false value (such as "None"), the given buffer is out-of-band;
   otherwise the buffer is serialized in-band, i.e. inside the pickle
   stream.

   It is an error if *buffer_callback* is not "None" and *protocol* is
   "None" or smaller than 5.

   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

      A pickler object's dispatch table is a registry of *reduction
      functions* of the kind which can be declared using
      "copyreg.pickle()".  It is a mapping whose keys are classes and
      whose values are reduction functions.  A reduction function
      takes a single argument of the associated class and should
      conform to the same interface as a "__reduce__()" method.

      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.

      Added in version 3.3.

   reducer_override(obj)

      Special reducer that can be defined in "Pickler" subclasses.
      This method has priority over any reducer in the
      "dispatch_table".  It should conform to the same interface as a
      "__reduce__()" method, and can optionally return
      "NotImplemented" to fallback on "dispatch_table"-registered
      reducers to pickle "obj".

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

      Added in 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.

   If *buffers* is "None" (the default), then all data necessary for
   deserialization must be contained in the pickle stream.  This means
   that the *buffer_callback* argument was "None" when a "Pickler" was
   instantiated (or when "dump()" or "dumps()" was called).

   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.

   Added in 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 :

* built-in constants ("None", "True", "False", "Ellipsis", and
  "NotImplemented");

* 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;

* instances of such classes whose the result of calling
  "__getstate__()" is picklable  (see section Sérialisation des
  instances d'une classe for details).

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.

Similarly, when class instances are pickled, their class's code and
data are not pickled along with them.  Only the instance data are
pickled.  This is done on purpose, so you can fix bugs in a class or
add methods to the class and still load objects that were created with
an earlier version of the class.  If you plan to have long-lived
objects that will see many versions of a class, it may be worthwhile
to put a version number in the objects so that suitable conversions
can be made by the class's "__setstate__()" method.


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.

In most cases, no additional code is needed to make instances
picklable.  By default, pickle will retrieve the class and the
attributes of an instance via introspection. When a class instance is
unpickled, its "__init__()" method is usually *not* invoked.  The
default behaviour first creates an uninitialized instance and then
restores the saved attributes.  The following code shows an
implementation of this behaviour:

   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__()

   Classes can further influence how their instances are pickled by
   overriding the method "__getstate__()".  It is called and the
   returned object is pickled as the contents for the instance,
   instead of a default state. There are several cases:

   * For a class that has no instance "__dict__" and no "__slots__",
     the default state is "None".

   * For a class that has an instance "__dict__" and no "__slots__",
     the default state is "self.__dict__".

   * For a class that has an instance "__dict__" and "__slots__", the
     default state is a tuple consisting of two dictionaries:
     "self.__dict__", and a dictionary mapping slot names to slot
     values.  Only slots that have a value are included in the latter.

   * For a class that has "__slots__" and no instance "__dict__", the
     default state is a tuple whose first item is "None" and whose
     second item is a dictionary mapping slot names to slot values
     described in the previous bullet.

   Modifié dans la version 3.11: Added the default implementation of
   the "__getstate__()" method in the "object" class.

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:

     If "__reduce__()" returns a state with value "None" at pickling,
     the "__setstate__()" method will not be called upon unpickling.

Refer to the section Traitement des objets à état for more information
about how to use the methods "__getstate__()" and "__setstate__()".

Note:

  At unpickling time, some methods like "__getattr__()",
  "__getattribute__()", or "__setattr__()" may be called upon the
  instance.  In case those methods rely on some internal invariant
  being true, the type should implement "__new__()" to establish such
  an invariant, as "__init__()" is not called when unpickling an
  instance.

As we shall see, pickle does not use directly the methods described
above.  In fact, these methods are part of the copy protocol which
implements the "__reduce__()" special method.  The copy protocol
provides a unified interface for retrieving the data necessary for
pickling and copying objects. [4]

Although powerful, implementing "__reduce__()" directly in your
classes is error prone.  For this reason, class designers should use
the high-level interface (i.e., "__getnewargs_ex__()",
"__getstate__()" and "__setstate__()") whenever possible.  We will
show, however, cases where using "__reduce__()" is the only option or
leads to more efficient pickling or both.

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__".

   * Optionally, an iterator (and not a sequence) yielding successive
     items. These items will be appended to the object either using
     "obj.append(item)" or, in batch, using
     "obj.extend(list_of_items)". This is primarily used for list
     subclasses, but may be used by other classes as long as they have
     append and extend methods with the appropriate signature.
     (Whether "append()" or "extend()" is used depends on which pickle
     protocol version is used as well as the number of items to
     append, so both must be supported.)

   * 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.

     Added in 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.

The global dispatch table managed by the "copyreg" module is available
as "copyreg.dispatch_table".  Therefore, one may choose to use a
modified copy of "copyreg.dispatch_table" as a private dispatch table.

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
----------------------------

Here's an example that shows how to modify pickling behavior for a
class. The "TextReader" class below opens a text file, and returns the
line number and line contents each time its "readline()" method is
called. If a "TextReader" instance is pickled, all attributes *except*
the file object member are saved. When the instance is unpickled, the
file is reopened, and reading resumes from the last location. The
"__setstate__()" and "__getstate__()" methods are used to implement
this behavior.

   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
==================================================================

Added in 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.

For those cases, it is possible to subclass from the "Pickler" class
and implement a "reducer_override()" method. This method can return an
arbitrary reduction tuple (see "__reduce__()"). It can alternatively
return "NotImplemented" to fallback to the traditional behavior.

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
==================

Added in 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
-------------------

The large data objects to be pickled must implement a
"__reduce_ex__()" method specialized for protocol 5 and higher, which
returns a "PickleBuffer" instance (instead of e.g. a "bytes" object)
for any large data.

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.
