pickle — Python-Objekt serialisierung¶
Quellcode: Lib/pickle.py
Das Modul pickle implementiert binäre Protokolle für die Serialisierung und Deserialisierung einer Python-Objektstruktur. „Pickling“ ist der Prozess, bei dem eine Python-Objekthierarchie in einen Byte-Stream umgewandelt wird, und „Unpickling“ ist die umgekehrte Operation, bei der ein Byte-Stream (aus einer Binärdatei oder einem Byte-ähnlichen Objekt) wieder in eine Objekt-Hierarchie umgewandelt wird. Pickling (und Unpickling) wird alternativ auch als „Serialisierung“, „Marshalling“ oder „Flattening“ bezeichnet[1]; um Verwirrung zu vermeiden, werden hier jedoch die Begriffe „Pickling“ und „Unpickling“ verwendet.
Warnung
Das Modul pickle ist **nicht sicher**. Entpickeln Sie nur Daten, denen Sie vertrauen.
Es ist möglich, bösartige Pickle-Daten zu erstellen, die **während des Entpickelns beliebigen Code ausführen** werden. Entpickeln Sie niemals Daten, die aus einer nicht vertrauenswürdigen Quelle stammen könnten oder manipuliert worden sein könnten.
Erwägen Sie die Signierung von Daten mit hmac, wenn Sie sicherstellen müssen, dass sie nicht manipuliert wurden.
Sicherere Serialisierungsformate wie json können besser geeignet sein, wenn Sie nicht vertrauenswürdige Daten verarbeiten. Siehe Vergleich mit json.
Beziehung zu anderen Python-Modulen¶
Vergleich mit marshal¶
Python hat ein primitiveres Serialisierungsmodul namens marshal, aber im Allgemeinen sollte pickle immer der bevorzugte Weg zur Serialisierung von Python-Objekten sein. marshal existiert hauptsächlich zur Unterstützung der .pyc-Dateien von Python.
Das Modul pickle unterscheidet sich in mehreren wesentlichen Punkten von marshal
Das Modul
pickleverfolgt die Objekte, die es bereits serialisiert hat, so dass spätere Referenzen auf dasselbe Objekt nicht erneut serialisiert werden.marshaltut dies nicht.Dies hat Auswirkungen sowohl auf rekursive Objekte als auch auf die gemeinsame Nutzung von Objekten. Rekursive Objekte sind Objekte, die Referenzen auf sich selbst enthalten. Diese werden von marshal nicht behandelt, und tatsächlich führt der Versuch, rekursive Objekte zu marshalisieren, zum Absturz Ihres Python-Interpreters. Die gemeinsame Nutzung von Objekten geschieht, wenn es mehrere Referenzen auf dasselbe Objekt an verschiedenen Stellen in der zu serialisierenden Objekt-Hierarchie gibt.
picklespeichert solche Objekte nur einmal und stellt sicher, dass alle anderen Referenzen auf die Master-Kopie zeigen. Gemeinsam genutzte Objekte bleiben gemeinsam genutzt, was für veränderliche Objekte sehr wichtig sein kann.marshalkann nicht zur Serialisierung von benutzerdefinierten Klassen und ihren Instanzen verwendet werden.picklekann Klasseninstanzen transparent speichern und wiederherstellen, jedoch muss die Klassendefinition importierbar sein und sich im selben Modul befinden wie zum Zeitpunkt der Speicherung des Objekts.Das Serialisierungsformat von
marshalist nicht garantiert portabel über Python-Versionen hinweg. Da seine Hauptaufgabe darin besteht,.pyc-Dateien zu unterstützen, behalten sich die Python-Entwickler das Recht vor, das Serialisierungsformat aus Gründen der Notwendigkeit nicht abwärtskompatibel zu ändern. Das Serialisierungsformat vonpickleist über Python-Releases hinweg abwärtskompatibel garantiert, vorausgesetzt, ein kompatibles Pickle-Protokoll wird gewählt und der Pickling- und Unpickling-Code berücksichtigt Python 2 zu Python 3 Typunterschiede, falls Ihre Daten diese einzigartige sprachliche Bruchgrenze überschreiten.
Vergleich mit json¶
Es gibt grundlegende Unterschiede zwischen den Pickle-Protokollen und JSON (JavaScript Object Notation)
JSON ist ein Textserialisierungsformat (es gibt Unicode-Text aus, obwohl es meistens zu
utf-8kodiert wird), während pickle ein binäres Serialisierungsformat ist;JSON ist menschenlesbar, pickle nicht;
JSON ist interoperabel und wird außerhalb des Python-Ökosystems weit verbreitet, während pickle Python-spezifisch ist;
JSON kann standardmäßig nur eine Teilmenge der Python-integrierten Typen und keine benutzerdefinierten Klassen darstellen; pickle kann eine extrem große Anzahl von Python-Typen darstellen (viele davon automatisch durch clevere Nutzung der Introspektionsfähigkeiten von Python; komplexe Fälle können durch Implementierung spezifischer Objekt-APIs behandelt werden);
Im Gegensatz zu pickle stellt die Deserialisierung von nicht vertrauenswürdigem JSON an sich keine Sicherheitslücke für die Ausführung beliebigen Codes dar.
Siehe auch
Das Modul json: Ein Modul der Standardbibliothek, das JSON-Serialisierung und -Deserialisierung ermöglicht.
Datenstromformat¶
Das von pickle verwendete Datenformat ist Python-spezifisch. Dies hat den Vorteil, dass keine Einschränkungen durch externe Standards wie JSON (das keine Zeigerteilung darstellen kann) auferlegt werden; es bedeutet jedoch, dass Nicht-Python-Programme möglicherweise keine gepickelten Python-Objekte rekonstruieren können.
Standardmäßig verwendet das Datenformat von pickle eine relativ kompakte binäre Darstellung. Wenn Sie optimale Größenmerkmale benötigen, können Sie gepickelte Daten effizient komprimieren.
Das Modul pickletools enthält Werkzeuge zur Analyse von Datenströmen, die von pickle generiert werden. Der Quellcode von pickletools enthält ausführliche Kommentare zu den von den Pickle-Protokollen verwendeten Opcodes.
Es gibt derzeit 6 verschiedene Protokolle, die für das Pickling verwendet werden können. Je höher das verwendete Protokoll, desto neuer ist die Python-Version, die zum Lesen des erzeugten Pickles benötigt wird.
Protokollversion 0 ist das ursprüngliche „menschenlesbare“ Protokoll und ist abwärtskompatibel mit früheren Versionen von Python.
Protokollversion 1 ist ein altes Binärformat, das ebenfalls mit früheren Versionen von Python kompatibel ist.
Protokollversion 2 wurde in Python 2.3 eingeführt. Es bietet eine wesentlich effizientere Serialisierung von New-Style-Klassen. Informationen über die von Protokoll 2 eingeführten Verbesserungen finden Sie in PEP 307.
Protokollversion 3 wurde in Python 3.0 hinzugefügt. Sie bietet explizite Unterstützung für
bytes-Objekte und kann nicht von Python 2.x entpickelt werden. Dies war das Standardprotokoll in Python 3.0–3.7.Protokollversion 4 wurde in Python 3.4 hinzugefügt. Sie bietet Unterstützung für sehr große Objekte, die Serialisierung weiterer Objekttypen und einige Datenformatoptimierungen. Dies war das Standardprotokoll in Python 3.8–3.13. Informationen über die von Protokoll 4 eingeführten Verbesserungen finden Sie in PEP 3154.
Protokollversion 5 wurde in Python 3.8 hinzugefügt. Sie bietet Unterstützung für Out-of-Band-Daten und Geschwindigkeitssteigerungen für In-Band-Daten. Dies ist das Standardprotokoll ab Python 3.14. Informationen über die von Protokoll 5 eingeführten Verbesserungen finden Sie in PEP 574.
Hinweis
Serialisierung ist ein primitiveres Konzept als Persistenz; obwohl pickle Dateiobjekte liest und schreibt, befasst es sich nicht mit dem Problem der Benennung persistenter Objekte oder dem (noch komplizierteren) Problem des gleichzeitigen Zugriffs auf persistente Objekte. Das Modul pickle kann ein komplexes Objekt in einen Byte-Stream umwandeln und aus dem Byte-Stream ein Objekt mit derselben internen Struktur machen. Das Offensichtlichste, was man mit diesen Byte-Streams tun kann, ist, sie in eine Datei zu schreiben, aber es ist auch denkbar, sie über ein Netzwerk zu senden oder in einer Datenbank zu speichern. Das Modul shelve bietet eine einfache Schnittstelle zum Pickling und Unpickling von Objekten in DBM-ähnlichen Datenbankdateien.
Modulschnittstelle¶
Um eine Objekt-Hierarchie zu serialisieren, rufen Sie einfach die Funktion dumps() auf. Umgekehrt rufen Sie zum Deserialisieren eines Datenstroms die Funktion loads() auf. Wenn Sie jedoch mehr Kontrolle über die Serialisierung und Deserialisierung wünschen, können Sie jeweils ein Objekt von Pickler oder Unpickler erstellen.
Das Modul pickle stellt die folgenden Konstanten bereit
- pickle.HIGHEST_PROTOCOL¶
Eine Ganzzahl, die höchste verfügbare Protokollversion. Dieser Wert kann als protocol-Wert an die Funktionen
dump()unddumps()sowie an den Konstruktor vonPicklerübergeben werden.
- pickle.DEFAULT_PROTOCOL¶
Eine Ganzzahl, die Standard-Protokollversion, die für das Pickling verwendet wird. Kann kleiner sein als
HIGHEST_PROTOCOL. Derzeit ist das Standardprotokoll 5, das in Python 3.8 eingeführt wurde und mit früheren Versionen inkompatibel ist. Diese Version führt die Unterstützung für Out-of-Band-Puffer ein, bei denen PEP 3118-kompatible Daten getrennt vom Haupt-Pickle-Stream übertragen werden können.Geändert in Version 3.0: Das Standardprotokoll ist 3.
Geändert in Version 3.8: Das Standardprotokoll ist 4.
Geändert in Version 3.14: Das Standardprotokoll ist 5.
Das Modul pickle stellt die folgenden Funktionen zur Verfügung, um den Pickling-Prozess bequemer zu gestalten
- pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)¶
Schreibt die gepickelte Darstellung des Objekts obj in das geöffnete Datei-Objekt file. Dies ist äquivalent zu
Pickler(file, protocol).dump(obj).Die Argumente file, protocol, fix_imports und buffer_callback haben dieselbe Bedeutung wie im Konstruktor von
Pickler.Geändert in Version 3.8: Das Argument buffer_callback wurde hinzugefügt.
- pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)¶
Gibt die gepickelte Darstellung des Objekts obj als
bytes-Objekt zurück, anstatt sie in eine Datei zu schreiben.Die Argumente protocol, fix_imports und buffer_callback haben dieselbe Bedeutung wie im Konstruktor von
Pickler.Geändert in Version 3.8: Das Argument buffer_callback wurde hinzugefügt.
- pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)¶
Liest die gepickelte Darstellung eines Objekts aus dem geöffneten Datei-Objekt file und gibt die darin spezifizierte wiederhergestellte Objekt-Hierarchie zurück. Dies ist äquivalent zu
Unpickler(file).load().Die Protokollversion des Pickles wird automatisch erkannt, daher ist kein Protokollargument erforderlich. Bytes nach der gepickelten Darstellung des Objekts werden ignoriert.
Die Argumente file, fix_imports, encoding, errors, strict und buffers haben dieselbe Bedeutung wie im Konstruktor von
Unpickler.Geändert in Version 3.8: Das Argument buffers wurde hinzugefügt.
- pickle.loads(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)¶
Gibt die wiederhergestellte Objekt-Hierarchie der gepickelten Darstellung data eines Objekts zurück. data muss ein Byte-ähnliches Objekt sein.
Die Protokollversion des Pickles wird automatisch erkannt, daher ist kein Protokollargument erforderlich. Bytes nach der gepickelten Darstellung des Objekts werden ignoriert.
Die Argumente fix_imports, encoding, errors, strict und buffers haben dieselbe Bedeutung wie im Konstruktor von
Unpickler.Geändert in Version 3.8: Das Argument buffers wurde hinzugefügt.
Das Modul pickle definiert drei Ausnahmen
- exception pickle.PickleError¶
Gemeinsame Basisklasse für die anderen Pickling-Ausnahmen. Sie erbt von
Exception.
- exception pickle.PicklingError¶
Fehler, der ausgelöst wird, wenn ein nicht pickelbares Objekt von
Picklerangetroffen wird. Sie erbt vonPickleError.Siehe Was kann gepickelt und entpickelt werden?, um zu erfahren, welche Arten von Objekten gepickelt werden können.
- exception pickle.UnpicklingError¶
Fehler, der ausgelöst wird, wenn ein Problem beim Entpickeln eines Objekts auftritt, wie z.B. eine Datenbeschädigung oder eine Sicherheitsverletzung. Sie erbt von
PickleError.Beachten Sie, dass während des Entpickelns auch andere Ausnahmen ausgelöst werden können, einschließlich (aber nicht notwendigerweise beschränkt auf) AttributeError, EOFError, ImportError und IndexError.
Das Modul pickle exportiert drei Klassen: Pickler, Unpickler und PickleBuffer
- class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)¶
Dies nimmt eine Binärdatei zum Schreiben eines Pickle-Datenstroms entgegen.
Das optionale Argument protocol, eine Ganzzahl, weist den Pickler an, das angegebene Protokoll zu verwenden; unterstützte Protokolle sind 0 bis
HIGHEST_PROTOCOL. Wenn nicht angegeben, ist der StandardwertDEFAULT_PROTOCOL. Wenn eine negative Zahl angegeben wird, wirdHIGHEST_PROTOCOLausgewählt.Das Argument file muss eine write()-Methode haben, die ein einzelnes Bytes-Argument akzeptiert. Es kann also eine auf der Festplatte geöffnete Datei für binäres Schreiben, eine
io.BytesIO-Instanz oder jedes andere benutzerdefinierte Objekt sein, das diese Schnittstelle erfüllt.Wenn fix_imports wahr ist und protocol kleiner als 3 ist, versucht pickle, die neuen Python 3-Namen auf die alten Modulnamen abzubilden, die in Python 2 verwendet wurden, damit der Pickle-Datenstrom mit Python 2 lesbar ist.
Wenn buffer_callback
None(Standard) ist, werden Pufferansichten als Teil des Pickle-Streams in file serialisiert.Wenn buffer_callback nicht
Noneist, kann es beliebig oft mit einer Pufferansicht aufgerufen werden. Wenn der Callback einen falschen Wert (wieNone) zurückgibt, ist der gegebene Puffer Out-of-Band; andernfalls wird der Puffer In-Band serialisiert, d.h. innerhalb des Pickle-Streams.Es ist ein Fehler, wenn buffer_callback nicht
Noneist und protocolNoneoder kleiner als 5 ist.Geändert in Version 3.8: Das Argument buffer_callback wurde hinzugefügt.
- dump(obj)¶
Schreibt die gepickelte Darstellung von obj in das im Konstruktor angegebene geöffnete Datei-Objekt.
- persistent_id(obj)¶
Tut standardmäßig nichts. Dies existiert, damit eine Unterklasse sie überschreiben kann.
Wenn
persistent_id()Nonezurückgibt, wird obj wie üblich gepickelt. Jeder andere Wert führt dazu, dassPicklerden zurückgegebenen Wert als persistenten ID für obj ausgibt. Die Bedeutung dieser persistenten ID sollte vonUnpickler.persistent_load()definiert werden. Beachten Sie, dass der vonpersistent_id()zurückgegebene Wert selbst keine persistente ID haben kann.Siehe Persistenz externer Objekte für Details und Anwendungsbeispiele.
Geändert in Version 3.13: Fügt die Standardimplementierung dieser Methode in der C-Implementierung von
Picklerhinzu.
- dispatch_table¶
Die Dispatch-Tabelle eines Pickler-Objekts ist eine Registrierung von Reduktionsfunktionen, wie sie mit
copyreg.pickle()deklariert werden können. Es ist eine Zuordnung, deren Schlüssel Klassen und deren Werte Reduktionsfunktionen sind. Eine Reduktionsfunktion nimmt ein einzelnes Argument der zugehörigen Klasse entgegen und sollte der gleichen Schnittstelle entsprechen wie eine__reduce__()-Methode.Standardmäßig hat ein Pickler-Objekt kein
dispatch_table-Attribut und verwendet stattdessen die globale Dispatch-Tabelle, die vomcopyreg-Modul verwaltet wird. Um das Pickling für ein bestimmtes Pickler-Objekt anzupassen, kann jedoch dasdispatch_table-Attribut auf ein Wörterbuch-ähnliches Objekt gesetzt werden. Wenn alternativ eine Unterklasse vonPicklereindispatch_table-Attribut hat, wird dieses als Standard-Dispatch-Tabelle für Instanzen dieser Klasse verwendet.Siehe Dispatch-Tabellen für Anwendungsbeispiele.
Hinzugefügt in Version 3.3.
- reducer_override(obj)¶
Spezieller Reducer, der in Unterklassen von
Picklerdefiniert werden kann. Diese Methode hat Vorrang vor jedem Reducer in derdispatch_table. Sie sollte der gleichen Schnittstelle entsprechen wie eine__reduce__()-Methode und kann optionalNotImplementedzurückgeben, um aufdispatch_tableregistrierte Reducer zurückzugreifen, umobjzu pickeln.Ein detailliertes Beispiel finden Sie unter Benutzerdefinierte Reduktion für Typen, Funktionen und andere Objekte.
Hinzugefügt in Version 3.8.
- fast¶
Veraltet. Aktiviert den Schnellmodus, wenn auf einen wahren Wert gesetzt. Der Schnellmodus deaktiviert die Verwendung des Memos und beschleunigt dadurch den Pickling-Prozess, indem er keine überflüssigen PUT-Opcodes generiert. Er sollte nicht mit sich selbst referenzierenden Objekten verwendet werden, andernfalls kommt es zu einer unendlichen Rekursion des
Pickler.Verwenden Sie
pickletools.optimize(), wenn Sie kompaktere Pickles benötigen.
- clear_memo()¶
Löscht das „Memo“ des Picklers.
Das Memo ist die Datenstruktur, die sich merkt, welche Objekte der Pickler bereits gesehen hat, damit gemeinsame oder rekursive Objekte als Referenz und nicht als Wert gepickelt werden. Diese Methode ist nützlich, wenn Pickler wiederverwendet werden.
- class pickle.Unpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)¶
Dies nimmt eine Binärdatei zum Lesen eines Pickle-Datenstroms entgegen.
Die Protokollversion des Pickles wird automatisch erkannt, sodass kein Protokollargument benötigt wird.
Das Argument file muss drei Methoden haben: eine read()-Methode, die ein ganzzahliges Argument annimmt, eine readinto()-Methode, die ein Pufferargument annimmt, und eine readline()-Methode, die keine Argumente benötigt, wie in der
io.BufferedIOBase-Schnittstelle. Somit kann file eine auf der Festplatte geöffnete Datei für die binäre Lesung, einio.BytesIO-Objekt oder jedes andere benutzerdefinierte Objekt sein, das diese Schnittstelle erfüllt.Die optionalen Argumente fix_imports, encoding und errors werden verwendet, um die Kompatibilitätsunterstützung für von Python 2 generierte Pickle-Streams zu steuern. Wenn fix_imports wahr ist, versucht Pickle, die alten Python-2-Namen auf die in Python 3 verwendeten neuen Namen abzubilden. encoding und errors teilen Pickle mit, wie 8-Bit-String-Instanzen, die von Python 2 gepickelt wurden, dekodiert werden sollen; diese sind standardmäßig „ASCII“ und „strict“. encoding kann „bytes“ sein, um diese 8-Bit-String-Instanzen als Bytes-Objekte zu lesen. Die Verwendung von
encoding='latin1'ist erforderlich, um NumPy-Arrays und Instanzen vondatetime,dateundtime, die von Python 2 gepickelt wurden, zu entpickeln.Wenn buffers
None(Standard) ist, müssen alle zur Deserialisierung erforderlichen Daten im Pickle-Stream enthalten sein. Das bedeutet, dass das Argument buffer_callbackNonewar, als einPicklerinstanziiert wurde (oder alsdump()oderdumps()aufgerufen wurde).Wenn buffers nicht
Noneist, sollte es ein iterierbares Objekt mit Pufferunterstützung sein, das jedes Mal verbraucht wird, wenn der Pickle-Stream eine Out-of-Band-Pufferansicht referenziert. Solche Puffer wurden dem buffer_callback eines Pickler-Objekts in der Reihenfolge übergeben.Geändert in Version 3.8: Das Argument buffers wurde hinzugefügt.
- load()¶
Liest die gepickelte Darstellung eines Objekts aus dem im Konstruktor angegebenen geöffneten Datei-Objekt und gibt die darin spezifizierte, wiederhergestellte Objekt-Hierarchie zurück. Bytes, die über die gepickelte Darstellung des Objekts hinausgehen, werden ignoriert.
- persistent_load(pid)¶
Löst standardmäßig eine
UnpicklingErroraus.Wenn definiert, sollte
persistent_load()das durch die persistente ID pid spezifizierte Objekt zurückgeben. Wenn eine ungültige persistente ID angetroffen wird, sollte eineUnpicklingErrorausgelöst werden.Siehe Persistenz externer Objekte für Details und Anwendungsbeispiele.
Geändert in Version 3.13: Fügt die Standardimplementierung dieser Methode in der C-Implementierung von
Unpicklerhinzu.
- find_class(module, name)¶
Importiert module, falls erforderlich, und gibt das von ihm aufgerufene Objekt name zurück, wobei die Argumente module und name
str-Objekte sind. Beachten Sie, dassfind_class()entgegen seinem Namen auch zum Finden von Funktionen verwendet wird.Unterklassen können diese Methode überschreiben, um zu steuern, welche Arten von Objekten und wie diese geladen werden können, was potenziell Sicherheitsrisiken reduziert. Einzelheiten finden Sie unter Einschränkung von Globals.
Löst ein Auditing-Event
pickle.find_classmit den Argumentenmodule,nameaus.
- class pickle.PickleBuffer(buffer)¶
Ein Wrapper für einen Puffer, der pickelbare Daten repräsentiert. buffer muss ein Objekt sein, das einen Puffer bereitstellt, wie z. B. ein bytes-ähnliches Objekt oder ein N-dimensionales Array.
PickleBufferselbst ist ein Pufferanbieter, daher ist es möglich, es an andere APIs zu übergeben, die ein Pufferanbieterobjekt erwarten, wie z. B.memoryview.PickleBuffer-Objekte können nur mit Pickle-Protokoll 5 oder höher serialisiert werden. Sie sind für die Out-of-Band-Serialisierung berechtigt.Hinzugefügt in Version 3.8.
- raw()¶
Gibt eine
memoryviewdes zugrunde liegenden Speicherbereichs dieses Puffers zurück. Das zurückgegebene Objekt ist eine eindimensionale, C-kontinuierliche Memoryview mit dem FormatB(unsigned bytes).BufferErrorwird ausgelöst, wenn der Puffer weder C- noch Fortran-kontinuierlich ist.
- release()¶
Gibt den vom PickleBuffer-Objekt bereitgestellten zugrunde liegenden Puffer frei.
Was kann gepickelt und entpickelt werden?¶
Die folgenden Typen können gepickelt werden
eingebaute Konstanten (
None,True,False,EllipsisundNotImplemented);ganze Zahlen, Gleitkommazahlen, komplexe Zahlen;
Strings, Bytes, Bytearrays;
Tupel, Listen, Mengen und Wörterbücher, die nur pickelbare Objekte enthalten;
Funktionen (eingebaut und benutzerdefiniert), die von der obersten Ebene eines Moduls aus zugänglich sind (mit
def, nicht mitlambda);Klassen, die von der obersten Ebene eines Moduls aus zugänglich sind;
Instanzen solcher Klassen, deren Ergebnis des Aufrufs von
__getstate__()pickelbar ist (siehe Abschnitt Pickling von Klasseninstanzen für Details).
Versuche, nicht pickelbare Objekte zu pickeln, lösen die Ausnahme PicklingError aus; wenn dies geschieht, können eine nicht spezifizierte Anzahl von Bytes bereits in die zugrunde liegende Datei geschrieben worden sein. Der Versuch, eine stark rekursive Datenstruktur zu pickeln, kann die maximale Rekursionstiefe überschreiten, in diesem Fall wird eine RecursionError ausgelöst. Sie können dieses Limit vorsichtig mit sys.setrecursionlimit() erhöhen.
Beachten Sie, dass Funktionen (eingebaut und benutzerdefiniert) mit ihrem vollständig qualifizierten Namen gepickelt werden, nicht per Wert. [2] Das bedeutet, dass nur der Funktionsname zusammen mit dem Namen des enthaltenden Moduls und der Klassen gepickelt wird. Weder der Code der Funktion noch ihre Funktionsattribute werden gepickelt. Daher muss das definierende Modul in der Entpicklungsumgebung importierbar sein, und das Modul muss das benannte Objekt enthalten, andernfalls wird eine Ausnahme ausgelöst. [3]
In ähnlicher Weise werden Klassen mit vollständig qualifizierten Namen gepickelt, sodass die gleichen Einschränkungen in der Entpicklungsumgebung gelten. Beachten Sie, dass kein Code oder keine Daten der Klasse mitgepickelt werden. Im folgenden Beispiel wird das Klassenattribut attr daher in der Entpicklungsumgebung nicht wiederhergestellt.
class Foo:
attr = 'A class attribute'
picklestring = pickle.dumps(Foo)
Diese Einschränkungen sind der Grund, warum pickelbare Funktionen und Klassen auf der obersten Ebene eines Moduls definiert sein müssen.
Ebenso werden bei der Pickling von Klasseninstanzen deren Klassencode und -daten nicht mitgepickelt. Nur die Instanzdaten werden gepickelt. Dies geschieht absichtlich, damit Sie Fehler in einer Klasse beheben oder der Klasse Methoden hinzufügen und dennoch Objekte laden können, die mit einer früheren Version der Klasse erstellt wurden. Wenn Sie vorhaben, langlebige Objekte zu haben, die viele Versionen einer Klasse sehen, kann es sich lohnen, eine Versionsnummer in die Objekte einzufügen, damit geeignete Konvertierungen durch die __setstate__()-Methode der Klasse vorgenommen werden können.
Pickling von Klasseninstanzen¶
In diesem Abschnitt beschreiben wir die allgemeinen Mechanismen, mit denen Sie definieren, anpassen und steuern können, wie Klasseninstanzen gepickelt und entpickelt werden.
In den meisten Fällen ist kein zusätzlicher Code erforderlich, um Instanzen pickelbar zu machen. Standardmäßig ruft Pickle die Klasse und die Attribute einer Instanz per Introspektion ab. Wenn eine Klasseninstanz entpickelt wird, wird ihre __init__()-Methode normalerweise *nicht* aufgerufen. Das Standardverhalten erstellt zuerst eine nicht initialisierte Instanz und stellt dann die gespeicherten Attribute wieder her. Der folgende Code zeigt eine Implementierung dieses Verhaltens.
def save(obj):
return (obj.__class__, obj.__dict__)
def restore(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
Klassen können das Standardverhalten ändern, indem sie eine oder mehrere spezielle Methoden bereitstellen
- object.__getnewargs_ex__()¶
In Protokoll 2 und neuer können Klassen, die die Methode
__getnewargs_ex__()implementieren, die Werte bestimmen, die beim Entpickeln an die__new__()-Methode übergeben werden. Die Methode muss ein Paar(args, kwargs)zurückgeben, wobei args ein Tupel von Positionsargumenten und kwargs ein Wörterbuch von benannten Argumenten für die Konstruktion des Objekts ist. Diese werden beim Entpickeln an die__new__()-Methode übergeben.Sie sollten diese Methode implementieren, wenn die
__new__()-Methode Ihrer Klasse schlüsselwortbasierte Argumente erfordert. Andernfalls wird für die Kompatibilität die Implementierung von__getnewargs__()empfohlen.Geändert in Version 3.6:
__getnewargs_ex__()wird jetzt in den Protokollen 2 und 3 verwendet.
- object.__getnewargs__()¶
Diese Methode hat einen ähnlichen Zweck wie
__getnewargs_ex__(), unterstützt aber nur Positionsargumente. Sie muss ein Tupel von Argumentenargszurückgeben, das beim Entpickeln an die__new__()-Methode übergeben wird.__getnewargs__()wird nicht aufgerufen, wenn__getnewargs_ex__()definiert ist.Geändert in Version 3.6: Vor Python 3.6 wurde
__getnewargs__()anstelle von__getnewargs_ex__()in den Protokollen 2 und 3 aufgerufen.
- object.__getstate__()¶
Klassen können weiter beeinflussen, wie ihre Instanzen gepickelt werden, indem sie die Methode
__getstate__()überschreiben. Sie wird aufgerufen und das zurückgegebene Objekt wird anstelle eines Standardzustands als Inhalt für die Instanz gepickelt. Es gibt mehrere Fälle:Für eine Klasse, die kein Instanz-
__dict__und keine__slots__hat, ist der StandardzustandNone.Für eine Klasse, die ein Instanz-
__dict__und keine__slots__hat, ist der Standardzustandself.__dict__.Für eine Klasse, die ein Instanz-
__dict__und__slots__hat, ist der Standardzustand ein Tupel, bestehend aus zwei Wörterbüchern:self.__dict__und ein Wörterbuch, das Slot-Namen mit Slot-Werten abbildet. Nur Slots, die einen Wert haben, werden in letzterem enthalten sein.Für eine Klasse, die
__slots__und keine Instanz-__dict__hat, ist der Standardzustand ein Tupel, dessen erstes ElementNoneist und dessen zweites Element ein Wörterbuch ist, das Slot-Namen mit Slot-Werten abbildet, wie im vorherigen Punkt beschrieben.
Geändert in Version 3.11: Fügt die Standardimplementierung der Methode
__getstate__()in der Klasseobjecthinzu.
- object.__setstate__(state)¶
Beim Entpickeln wird, wenn die Klasse
__setstate__()definiert, diese mit dem entpickelten Zustand aufgerufen. In diesem Fall gibt es keine Anforderung, dass das Zustands-Objekt ein Wörterbuch ist. Andernfalls muss der gepickelte Zustand ein Wörterbuch sein und seine Elemente werden dem Wörterbuch der neuen Instanz zugewiesen.Hinweis
Wenn
__reduce__()beim Pickling einen Zustand mit dem WertNonezurückgibt, wird die Methode__setstate__()beim Entpickeln nicht aufgerufen.
Weitere Informationen zur Verwendung der Methoden __getstate__() und __setstate__() finden Sie im Abschnitt Umgang mit zustandsbehafteten Objekten.
Hinweis
Beim Entpickeln können einige Methoden wie __getattr__(), __getattribute__() oder __setattr__() auf die Instanz angewendet werden. Falls diese Methoden von einer internen Invariante abhängen, sollte der Typ __new__() implementieren, um eine solche Invariante herzustellen, da __init__() beim Entpickeln einer Instanz nicht aufgerufen wird.
Wie wir sehen werden, verwendet Pickle die oben beschriebenen Methoden nicht direkt. Tatsächlich sind diese Methoden Teil des Kopierprotokolls, das die spezielle Methode __reduce__() implementiert. Das Kopierprotokoll bietet eine einheitliche Schnittstelle zum Abrufen der Daten, die für das Pickling und Kopieren von Objekten erforderlich sind. [4]
Obwohl leistungsstark, ist die Implementierung von __reduce__() direkt in Ihren Klassen fehleranfällig. Aus diesem Grund sollten Klassenentwickler, wann immer möglich, die High-Level-Schnittstelle (d. h. __getnewargs_ex__(), __getstate__() und __setstate__()) verwenden. Wir werden jedoch Fälle zeigen, in denen die Verwendung von __reduce__() die einzige Option ist oder zu effizienterem Pickling führt oder beides.
- object.__reduce__()¶
Die Schnittstelle ist derzeit wie folgt definiert. Die Methode
__reduce__()nimmt keine Argumente entgegen und gibt entweder eine Zeichenkette oder vorzugsweise ein Tupel zurück (das zurückgegebene Objekt wird oft als „reduce value“ bezeichnet).Wenn eine Zeichenkette zurückgegeben wird, sollte die Zeichenkette als Name einer globalen Variablen interpretiert werden. Es sollte der lokale Name des Objekts relativ zu seinem Modul sein; das Pickle-Modul durchsucht den Modul-Namespace, um das Modul des Objekts zu bestimmen. Dieses Verhalten ist typischerweise für Singletons nützlich.
Wenn ein Tupel zurückgegeben wird, muss es zwischen zwei und sechs Elemente lang sein. Optionale Elemente können entweder weggelassen oder als ihr Wert
Nonezugewiesen werden. Die Semantik jedes Elements ist in der Reihenfolge:Ein aufrufbares Objekt, das aufgerufen wird, um die anfängliche Version des Objekts zu erstellen.
Ein Tupel von Argumenten für das aufrufbare Objekt. Ein leeres Tupel muss angegeben werden, wenn der Aufrufbare keine Argumente akzeptiert.
Optional der Zustand des Objekts, der an die Methode
__setstate__()des Objekts übergeben wird, wie zuvor beschrieben. Wenn das Objekt keine solche Methode hat, muss der Wert ein Dictionary sein und wird dem Attribut__dict__des Objekts hinzugefügt.Optional ein Iterator (und keine Sequenz), der aufeinanderfolgende Elemente liefert. Diese Elemente werden dem Objekt entweder über
obj.append(item)oder in Stapeln überobj.extend(list_of_items)hinzugefügt. Dies wird hauptsächlich für Listen-Subklassen verwendet, kann aber auch von anderen Klassen verwendet werden, solange diese Methodenappend()undextend()mit der entsprechenden Signatur haben. (Obappend()oderextend()verwendet wird, hängt von der verwendeten Pickle-Protokollversion sowie der Anzahl der hinzuzufügenden Elemente ab, daher müssen beide unterstützt werden.)Optional ein Iterator (keine Sequenz), der aufeinanderfolgende Schlüssel-Wert-Paare liefert. Diese Elemente werden dem Objekt über
obj[key] = valuezugewiesen. Dies wird hauptsächlich für Dictionary-Subklassen verwendet, kann aber auch von anderen Klassen verwendet werden, solange sie__setitem__()implementieren.Optional ein Aufrufbare mit einer Signatur
(obj, state). Dieses Aufrufbare ermöglicht es dem Benutzer, das Zustands-Update-Verhalten eines bestimmten Objekts programmatisch zu steuern, anstatt die statische Methode__setstate__()vonobjzu verwenden. Wenn nichtNone, hat dieses Aufrufbare Vorrang vor der__setstate__()-Methode vonobj.Hinzugefügt in Version 3.8: Das optionale sechste Tupel-Element,
(obj, state), wurde hinzugefügt.
- object.__reduce_ex__(protocol)¶
Alternativ kann eine Methode
__reduce_ex__()definiert werden. Der einzige Unterschied besteht darin, dass diese Methode ein einzelnes ganzzahliges Argument, die Protokollversion, entgegennehmen sollte. Wenn sie definiert ist, wird pickle sie der Methode__reduce__()vorziehen. Außerdem wird__reduce__()automatisch zu einem Synonym für die erweiterte Version. Der Hauptnutzen dieser Methode besteht darin, abwärtskompatible Reduce-Werte für ältere Python-Versionen bereitzustellen.
Persistenz externer Objekte¶
Zum Nutzen der Objektpersistenz unterstützt das Modul pickle die Vorstellung einer Referenz auf ein Objekt außerhalb des gepickelten Datenstroms. Solche Objekte werden durch eine persistente ID referenziert, die entweder eine Zeichenkette aus alphanumerischen Zeichen (für Protokoll 0) [5] oder ein beliebiges Objekt (für neuere Protokolle) sein sollte.
Die Auflösung solcher persistenter IDs ist nicht durch das Modul pickle definiert; es wird diese Auflösung an die benutzerdefinierten Methoden auf dem Pickler und Unpickler delegieren: persistent_id() und persistent_load().
Um Objekte zu picklen, die eine externe persistente ID haben, muss der Pickler eine benutzerdefinierte Methode persistent_id() haben, die ein Objekt als Argument nimmt und entweder None oder die persistente ID für dieses Objekt zurückgibt. Wenn None zurückgegeben wird, pickelt der Pickler das Objekt einfach wie gewohnt. Wenn eine persistente ID-Zeichenkette zurückgegeben wird, pickelt der Pickler dieses Objekt zusammen mit einem Marker, damit der Unpickler es als persistente ID erkennt.
Um externe Objekte zu entpickeln, muss der Unpickler eine benutzerdefinierte Methode persistent_load() haben, die ein persistentes ID-Objekt nimmt und das referenzierte Objekt zurückgibt.
Hier ist ein umfassendes Beispiel, das zeigt, wie eine persistente ID verwendet werden kann, um externe Objekte per Referenz zu picklen.
# 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()
Dispatch-Tabellen¶
Wenn man das Pickling bestimmter Klassen anpassen möchte, ohne anderen Code, der vom Pickling abhängt, zu stören, kann man einen Pickler mit einer privaten Dispatch-Tabelle erstellen.
Die globale Dispatch-Tabelle, die vom Modul copyreg verwaltet wird, ist als copyreg.dispatch_table verfügbar. Daher kann man wählen, eine modifizierte Kopie von copyreg.dispatch_table als private Dispatch-Tabelle zu verwenden.
Zum Beispiel
f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = reduce_SomeClass
erstellt eine Instanz von pickle.Pickler mit einer privaten Dispatch-Tabelle, die die Klasse SomeClass speziell behandelt. Alternativ der Code
class MyPickler(pickle.Pickler):
dispatch_table = copyreg.dispatch_table.copy()
dispatch_table[SomeClass] = reduce_SomeClass
f = io.BytesIO()
p = MyPickler(f)
tut dasselbe, aber alle Instanzen von MyPickler teilen standardmäßig die private Dispatch-Tabelle. Auf der anderen Seite der Code
copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)
modifiziert die globale Dispatch-Tabelle, die von allen Benutzern des Moduls copyreg gemeinsam genutzt wird.
Umgang mit zustandsbehafteten Objekten¶
Hier ist ein Beispiel, das zeigt, wie das Pickling-Verhalten für eine Klasse modifiziert werden kann. Die untenstehende Klasse TextReader öffnet eine Textdatei und gibt bei jedem Aufruf ihrer Methode readline() die Zeilennummer und den Zeileninhalt zurück. Wenn eine Instanz von TextReader gepickelt wird, werden alle Attribute *außer* dem Dateiobjektmitglied gespeichert. Wenn die Instanz entpickelt wird, wird die Datei neu geöffnet und die Leseposition wird vom letzten Ort fortgesetzt. Die Methoden __setstate__() und __getstate__() werden verwendet, um dieses Verhalten zu implementieren.
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
Eine Beispielverwendung könnte so aussehen
>>> 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!'
Benutzerdefinierte Reduktion für Typen, Funktionen und andere Objekte¶
Hinzugefügt in Version 3.8.
Manchmal ist die dispatch_table nicht flexibel genug. Insbesondere möchten wir das Pickling basierend auf einem anderen Kriterium als dem Objekttyp anpassen, oder wir möchten das Pickling von Funktionen und Klassen anpassen.
Für diese Fälle ist es möglich, von der Klasse Pickler zu erben und eine Methode reducer_override() zu implementieren. Diese Methode kann ein beliebiges Reduktionstupel zurückgeben (siehe __reduce__()). Sie kann alternativ NotImplemented zurückgeben, um auf das traditionelle Verhalten zurückzugreifen.
Wenn sowohl die dispatch_table als auch reducer_override() definiert sind, hat die Methode reducer_override() Vorrang.
Hinweis
Aus Leistungsgründen wird reducer_override() möglicherweise nicht für die folgenden Objekte aufgerufen: None, True, False und exakte Instanzen von int, float, bytes, str, dict, set, frozenset, list und tuple.
Hier ist ein einfaches Beispiel, bei dem wir das Pickling und die Rekonstruktion einer bestimmten Klasse zulassen
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
Out-of-Band-Puffer¶
Hinzugefügt in Version 3.8.
In einigen Kontexten wird das Modul pickle zum Übertragen massiver Datenmengen verwendet. Daher kann es wichtig sein, die Anzahl der Speicherkopien zu minimieren, um Leistung und Ressourcenverbrauch zu erhalten. Der normale Betrieb des Moduls pickle, das eine graphenartige Struktur von Objekten in einen sequenziellen Byte-Strom umwandelt, beinhaltet jedoch intrinsisch das Kopieren von Daten zum und vom Pickle-Strom.
Diese Einschränkung kann umgangen werden, wenn sowohl der *Anbieter* (die Implementierung der zu übertragenden Objekttypen) als auch der *Verbraucher* (die Implementierung des Kommunikationssystems) die Out-of-Band-Übertragungseinrichtungen unterstützen, die durch Pickle-Protokoll 5 und höher bereitgestellt werden.
Provider-API¶
Die zu pickelnden großen Datenobjekte müssen eine Methode __reduce_ex__() implementieren, die für Protokoll 5 und höher spezialisiert ist und eine PickleBuffer-Instanz (anstelle z. B. eines bytes-Objekts) für alle großen Daten zurückgibt.
Ein PickleBuffer-Objekt *signalisiert*, dass der zugrunde liegende Puffer für die Out-of-Band-Datenübertragung berechtigt ist. Diese Objekte bleiben mit der normalen Verwendung des Moduls pickle kompatibel. Verbraucher können jedoch auch opt-in machen, um pickle mitzuteilen, dass sie diese Puffer selbst behandeln werden.
Consumer-API¶
Ein Kommunikationssystem kann die benutzerdefinierte Handhabung von PickleBuffer-Objekten ermöglichen, die beim Serialisieren eines Objektgraphen generiert werden.
Auf der Senderseite muss ein Argument `buffer_callback` an Pickler (oder an die Funktion dump() oder dumps()) übergeben werden, die mit jedem PickleBuffer aufgerufen wird, der beim Pickling des Objektgraphen generiert wird. Puffer, die vom `buffer_callback` gesammelt werden, werden nicht in den Pickle-Strom kopiert, sondern nur ein billiger Marker eingefügt.
Auf der Empfängerseite muss ein Argument `buffers` an Unpickler (oder an die Funktion load() oder loads()) übergeben werden, welches ein iterierbares Objekt der Puffer ist, die an `buffer_callback` übergeben wurden. Dieses iterierbare Objekt sollte Puffer in derselben Reihenfolge liefern, wie sie an `buffer_callback` übergeben wurden. Diese Puffer stellen die Daten bereit, die von den Rekonstruktoren der Objekte erwartet werden, deren Pickling die ursprünglichen PickleBuffer-Objekte erzeugt hat.
Zwischen der Senderseite und der Empfängerseite kann das Kommunikationssystem seinen eigenen Übertragungsmechanismus für Out-of-Band-Puffer implementieren. Mögliche Optimierungen umfassen die Verwendung von Shared Memory oder datensatzabhängiger Komprimierung.
Beispiel¶
Hier ist ein triviales Beispiel, bei dem wir eine bytearray-Unterklasse implementieren, die an Out-of-Band-Buffer-Pickling teilnehmen kann
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)
Der Rekonstruktor (die Klassenmethode _reconstruct) gibt das Puffer-bereitstellende Objekt zurück, wenn es den richtigen Typ hat. Dies ist ein einfacher Weg, Zero-Copy-Verhalten in diesem Spielbeispiel zu simulieren.
Auf der Verbraucherseite können wir diese Objekte wie üblich picklen, was uns beim Deserialisieren eine Kopie des ursprünglichen Objekts liefert.
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
Aber wenn wir einen `buffer_callback` übergeben und die gesammelten Puffer beim Deserialisieren zurückgeben, können wir das ursprüngliche Objekt zurückbekommen.
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
Dieses Beispiel wird dadurch eingeschränkt, dass bytearray seinen eigenen Speicher allokiert: Sie können keine bytearray-Instanz erstellen, die von dem Speicher eines anderen Objekts unterstützt wird. Drittanbieter-Datentypen wie NumPy-Arrays haben jedoch diese Einschränkung nicht und ermöglichen die Verwendung von Zero-Copy-Pickling (oder die Erzeugung so weniger Kopien wie möglich) bei der Übertragung zwischen verschiedenen Prozessen oder Systemen.
Siehe auch
PEP 574 – Pickle-Protokoll 5 mit Out-of-Band-Daten
Einschränken von Globals¶
Standardmäßig importiert das Entpickeln jede Klasse oder Funktion, die es in den Pickle-Daten findet. Für viele Anwendungen ist dieses Verhalten inakzeptabel, da es dem Unpickler erlaubt, beliebigen Code zu importieren und aufzurufen. Betrachten Sie einfach, was dieser handgefertigte Pickle-Datenstrom tut, wenn er geladen wird.
>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0
In diesem Beispiel importiert der Unpickler die Funktion os.system() und wendet dann das String-Argument "echo hello world" an. Obwohl dieses Beispiel harmlos ist, ist es nicht schwer, sich eines vorzustellen, das Ihr System beschädigen könnte.
Aus diesem Grund möchten Sie möglicherweise steuern, was entpickelt wird, indem Sie Unpickler.find_class() anpassen. Entgegen seinem Namen wird Unpickler.find_class() aufgerufen, wenn ein Global (d. h. eine Klasse oder Funktion) angefordert wird. Daher ist es möglich, Globals entweder vollständig zu verbieten oder sie auf eine sichere Teilmenge zu beschränken.
Hier ist ein Beispiel für einen Unpickler, der nur wenige sichere Klassen aus dem Modul builtins laden lässt.
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()
Eine Beispielverwendung unseres Unpicklers, der wie beabsichtigt funktioniert.
>>> 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
Wie unsere Beispiele zeigen, müssen Sie vorsichtig sein, was Sie zum Entpickeln zulassen. Wenn Sicherheit ein Anliegen ist, sollten Sie daher Alternativen wie die Marshaling-API in xmlrpc.client oder Drittanbieterlösungen in Betracht ziehen.
Leistung¶
Neuere Versionen des Pickle-Protokolls (ab Protokoll 2) verfügen über effiziente Binärcodierungen für mehrere gängige Features und eingebaute Typen. Außerdem verfügt das Modul pickle über einen transparenten Optimierer, der in C geschrieben ist.
Beispiele¶
Für den einfachsten Code verwenden Sie die Funktionen dump() und 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)
Das folgende Beispiel liest die resultierenden gepickelten Daten.
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)
Kommandozeilenschnittstelle¶
Das Modul pickle kann als Skript von der Kommandozeile aufgerufen werden, es zeigt den Inhalt von Pickle-Dateien an. Wenn die zu untersuchende Pickle-Datei jedoch aus einer nicht vertrauenswürdigen Quelle stammt, ist -m pickletools eine sicherere Option, da es keinen Pickle-Bytecode ausführt. Siehe pickletools CLI-Nutzung.
python -m pickle pickle_file [pickle_file ...]
Die folgende Option wird akzeptiert
- pickle_file¶
Eine Pickle-Datei zum Lesen oder
-, um das Lesen von Standardeingabe anzuzeigen.
Siehe auch
- Modul
copyreg Registrierung von Pickle-Schnittstellenkonstruktoren für Erweiterungstypen.
- Modul
pickletools Werkzeuge für die Arbeit mit und die Analyse von gepickelten Daten.
- Modul
shelve Indexierte Datenbanken von Objekten; verwendet
pickle.- Modul
copy Flache und tiefe Kopieroperationen.
- Modul
marshal Hochleistungs-Serialisierung von eingebauten Typen.
Fußnoten