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 pickle verfolgt die Objekte, die es bereits serialisiert hat, so dass spätere Referenzen auf dasselbe Objekt nicht erneut serialisiert werden. marshal tut 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. pickle speichert 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.

  • marshal kann nicht zur Serialisierung von benutzerdefinierten Klassen und ihren Instanzen verwendet werden. pickle kann 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 marshal ist 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 von pickle ist ü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-8 kodiert 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() und dumps() sowie an den Konstruktor von Pickler ü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 Pickler angetroffen wird. Sie erbt von PickleError.

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 Standardwert DEFAULT_PROTOCOL. Wenn eine negative Zahl angegeben wird, wird HIGHEST_PROTOCOL ausgewä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 None ist, kann es beliebig oft mit einer Pufferansicht aufgerufen werden. Wenn der Callback einen falschen Wert (wie None) 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 None ist und protocol None oder 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() None zurückgibt, wird obj wie üblich gepickelt. Jeder andere Wert führt dazu, dass Pickler den zurückgegebenen Wert als persistenten ID für obj ausgibt. Die Bedeutung dieser persistenten ID sollte von Unpickler.persistent_load() definiert werden. Beachten Sie, dass der von persistent_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 Pickler hinzu.

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 vom copyreg-Modul verwaltet wird. Um das Pickling für ein bestimmtes Pickler-Objekt anzupassen, kann jedoch das dispatch_table-Attribut auf ein Wörterbuch-ähnliches Objekt gesetzt werden. Wenn alternativ eine Unterklasse von Pickler ein dispatch_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 Pickler definiert werden kann. Diese Methode hat Vorrang vor jedem Reducer in der dispatch_table. Sie sollte der gleichen Schnittstelle entsprechen wie eine __reduce__()-Methode und kann optional NotImplemented zurückgeben, um auf dispatch_table registrierte Reducer zurückzugreifen, um obj zu 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, ein io.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 von datetime, date und time, 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_callback None war, als ein Pickler instanziiert wurde (oder als dump() oder dumps() aufgerufen wurde).

Wenn buffers nicht None ist, 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 UnpicklingError aus.

Wenn definiert, sollte persistent_load() das durch die persistente ID pid spezifizierte Objekt zurückgeben. Wenn eine ungültige persistente ID angetroffen wird, sollte eine UnpicklingError ausgelö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 Unpickler hinzu.

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, dass find_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_class mit den Argumenten module, name aus.

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.

PickleBuffer selbst 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 memoryview des zugrunde liegenden Speicherbereichs dieses Puffers zurück. Das zurückgegebene Objekt ist eine eindimensionale, C-kontinuierliche Memoryview mit dem Format B (unsigned bytes). BufferError wird 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, Ellipsis und NotImplemented);

  • 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 mit lambda);

  • 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 Argumenten args zurü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 Standardzustand None.

  • Für eine Klasse, die ein Instanz-__dict__ und keine __slots__ hat, ist der Standardzustand self.__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 Element None ist 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 Klasse object hinzu.

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 Wert None zurü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 None zugewiesen 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 über obj.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 Methoden append() und extend() mit der entsprechenden Signatur haben. (Ob append() oder extend() 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] = value zugewiesen. 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__() von obj zu verwenden. Wenn nicht None, hat dieses Aufrufbare Vorrang vor der __setstate__()-Methode von obj.

    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