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

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

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.

Nouveau dans la 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é.

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 :

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

    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.

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

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.

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

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

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