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 formatpickle
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¶
The data format used by pickle
is Python-specific. This has the
advantage that there are no restrictions imposed by external standards such as
JSON (which can't represent pointer sharing); however it means that
non-Python programs may not be able to reconstruct pickled Python objects.
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()
etdumps()
ainsi qu'au constructeur de la classePickler
.
- 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 fromPickleError
.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
etIndexError
(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 deDEFAULT_PROTOCOL
. Avec un nombre strictement négatif, c'estHIGHEST_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éthodewrite()
acceptant d'être appelée sur un argument unique de typebytes
.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 asNone
), 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 isNone
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()
renvoieNone
, obj est sérialisé normalement. Toute autre valeur est reprise par lePickler
comme ID persistant pour obj. Le sens de cet ID persistant doit être défini parUnpickler.persistent_load()
. Veuillez noter que la valeur renvoyée parpersistent_id()
ne peut pas porter elle-même d'ID persistant.La section Persistance d'objets externes donne des détails et exemples.
Modifié dans la version 3.13: Add the default implementation of this method in the C implementation of
Pickler
.
- 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 modulecopyreg
. Afin de personnaliser l'opération de sérialisation pour un sérialiseur particulier, on peut affecter à son attributdispatch_table
un objet compatible avec les dictionnaires. Une autre possibilité est de définir l'attributdispatch_table
dans une classe fille dePickler
. Sa valeur sera alors utilisée pour toutes les instances de cette classe fille.Voir Tables de distribution pour des exemples d'utilisation.
Ajouté 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 thedispatch_table
. It should conform to the same interface as a__reduce__()
method, and can optionally returnNotImplemented
to fallback ondispatch_table
-registered reducers to pickleobj
.Voir Réduction personnalisée pour les types, fonctions et autres objets pour un exemple détaillé.
Ajouté 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, etreadline()
, sans arguments. file peut donc être aussi bien un fichier sur disque ouvert en mode lecture binaire qu'un objetio.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 dedatetime
,date
ettime
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 wasNone
when aPickler
was instantiated (or whendump()
ordumps()
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 exceptionUnpicklingError
.La section Persistance d'objets externes donne des détails et exemples.
Modifié dans la version 3.13: Add the default implementation of this method in the C implementation of
Unpickler
.
- 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 argumentsmodule
etname
.
- 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, commememoryview
.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.Ajouté 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 formatB
(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
, andNotImplemented
);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
, notlambda
);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 seraclasse.__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-upletargs
des arguments passés à__new__()
lors de la désérialisation : l'appel seraclasse.__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 isNone
.For a class that has an instance
__dict__
and no__slots__
, the default state isself.__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 isNone
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 theobject
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 valueNone
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, usingobj.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. (Whetherappend()
orextend()
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 dedict
, 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.Ajouté 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¶
Ajouté 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¶
Ajouté 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