Heim >Backend-Entwicklung >Python-Tutorial >Objektserialisierung und Deserialisierung der in Python integrierten Pickle-Bibliothek
Wir möchten kürzlich die vom Crawler erhaltenen Download-Ergebnisse archivieren. Dieses Ergebnis ist ein Python-Objekt (wir möchten nicht einfach ein HTML oder JSON speichern, sondern den gesamten Download-Prozess durchführen). reversibel), daher dachte ich darüber nach, die in Python integrierte Pickle-Bibliothek (Pickle-Bibliothek) zu verwenden, um Objekte in Bytes zu serialisieren und bei Bedarf zu deserialisieren.
Sie können die Verwendung und Funktion von Pickle einfach anhand des folgenden Codes verstehen.
In [2]: import pickle In [3]: class A: pass In [4]: a = A() In [5]: a.foo = 'hello' In [6]: a.bar = 2 In [7]: pick_ed = pickle.dumps(a) In [8]: pick_ed Out[8]: b'\x80\x03c__main__\nA\nq\x00)\x81q\x01}q\x02(X\x03\x00\x00\x00fooq\x03X\x05\x00\x00\x00helloq\x04X\x03\x00\x00\x00barq\x05K\x02ub.' In [9]: unpick = pickle.loads(pick_ed) In [10]: unpick Out[10]: <__main__.A at 0x10ae67278> In [11]: a Out[11]: <__main__.A at 0x10ae67128> In [12]: dir(unpick) Out[12]: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo'] In [13]: unpick.foo Out[13]: 'hello' In [14]: unpick.bar Out[14]: 2
Sie können sehen, dass die Verwendung von pickle in gewisser Weise der von json ähnelt, es gibt jedoch mehrere grundlegende Unterschiede:
json ist ein sprachübergreifendes universelles Datenaustauschformat, das im Allgemeinen in Textform ausgedrückt wird. Menschen lesbar. Pickle wird zum Serialisieren von Python-Objekten verwendet, nur für Python. Das Ergebnis der Serialisierung sind Binärdaten, die für Menschen nicht lesbar sind. Darüber hinaus kann JSON standardmäßig nur einen Teil der integrierten Typen serialisieren, und Pickle kann ziemlich viele Daten serialisieren.
Es ist auch ein alter Marschall eingebaut. Aber diese Bibliothek ist hauptsächlich für .pyc-Dateien gedacht. Benutzerdefinierte Typen werden nicht unterstützt und sind nicht vollständig. Beispielsweise kann es keine zyklischen Anwendungen verarbeiten, wenn ein Objekt auf sich selbst verweist, bleibt der Python-Interpreter bei der Verwendung von Marshal hängen.
Probleme mit der Versionskompatibilität
Da Pickle für Python ist, gibt es für Python verschiedene Versionen (und der Unterschied zwischen 2 und 3 ist sehr groß), daher muss dies berücksichtigt werden Überlegung Kann das serialisierte Objekt durch eine höhere (oder niedrigere?) Version von Python deserialisiert werden.
Derzeit gibt es 5 Pickle-Protokollversionen, desto höher ist die Python-Version 0-2 für Python3 und 3-4 für Python3.
Protocol version 0 is the original “human-readable” protocol and is backwards compatible with earlier versions of Python.Protocol version 1 is an old binary format which is also compatible with earlier versions of Python.Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307for information about improvements brought by protocol 2. (从这个版本往后,性能有显著提高)Protocol version 3 was added in Python 3.0. It has explicit support for bytes objects and cannot be unpickled by Python 2.x.This is the default protocol, and the recommended protocol when compatibility with other Python 3 versions is required.Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. Refer to PEP 3154 for information about improvements brought byprotocol 4.
Die meisten davon Eingänge zu Pickle-Funktionen (wie dump(), dumps(), Pickler-Konstruktor) akzeptieren alle einen Protokollversionsparameter, der über zwei integrierte Variablen verfügt:
pickle.HIGHEST_PROTOCOL ist derzeit 4
pickle.DEFAULT_PROTOCOL ist derzeit 3
Verwendung
ähnelt der integrierten JSON-Modulschnittstelle, dumps() wird verwendet, um das Serialisierungsergebnis zurückzugeben, dump( ) wird verwendet, um die Datei zu serialisieren und dann zu schreiben. Ebenso gibt es Load() und Load(). Unter anderem können Sie beim Serialisieren von Dumps die Protokollversion angeben, beim Deserialisieren ist dies jedoch nicht erforderlich und die Version wird automatisch identifiziert. Dies ist dem Befehl zip sehr ähnlich.
Serialisierung integrierter Typen
Die meisten integrierten Typen unterstützen Serialisierung und Deserialisierung. Was besondere Aufmerksamkeit erfordert, sind Funktionen. Die Serialisierung einer Funktion basiert lediglich auf ihrem Namen und dem Modul, in dem sie sich befindet. Weder der Code der Funktion noch ihre Attribute (Python-Funktionen sind erstklassige Objekte und können Attribute haben) werden nicht serialisiert. Dies erfordert, dass das Modul, in dem sich die Funktion befindet, in der Unpickle-Umgebung importierbar sein muss, andernfalls tritt ImportError oder AttributeError auf.
Hier gibt es etwas Interessantes: Alle Lambda-Funktionen sind nicht pickleable. Denn ihre Namen heißen
Serialisierung benutzerdefinierter Typen
Wie der experimentelle Code am Anfang dieses Artikels, in den meisten Fällen ohne zusätzliche Die Operation kann die Serialisierungs-/Deserialisierungsoperation realisieren . Es ist zu beachten, dass während des Deserialisierungsprozesses nicht __init__() der Klasse aufgerufen wird, um ein Objekt zu initialisieren, sondern eine neue nicht initialisierte Instanz erstellt wird und dann ihre Attribute wiederhergestellt werden (sehr clever). Der Pseudocode lautet wie folgt:
def save(obj): return (obj.__class__, obj.__dict__) def load(cls, attributes): obj = cls.__new__(cls) obj.__dict__.update(attributes) return obj
Wenn Sie während des Serialisierungsprozesses einige zusätzliche Vorgänge ausführen möchten, z. B. das Speichern des Status des Objekts, können Sie die magischen Methoden des Pickle-Protokolls verwenden, die am häufigsten vorkommen sind __setstate__() und __getstate__( ).
Sicherheitsprobleme (!)
Am Anfang der Pickle-Dokumentation heißt es: Entpicken Sie niemals eine Binärdatei aus einer unbekannten Quelle. Betrachten Sie den folgenden Code:
>>> import pickle >>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.") hello world 0
Wenn dieser Code nicht ausgewählt ist, importiert er os.system() und ruft dann echo auf. Es gibt keine Nebenwirkungen. Aber was ist, wenn es rm -rf /· ist?
Der Vorschlag des Dokuments besteht darin, die Prüflogik in Unpickler.find_class() zu implementieren. Funktionsmethoden müssen aufgerufen werden, wenn globale Variablen erforderlich sind.
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()
Komprimierung
Ich finde dieses Design sehr gut . Die Komprimierung wird anderen Bibliotheken überlassen. Und Sie können auch feststellen, dass die Datei nach dem Beizen zwar nicht lesbar ist, der Inhalt jedoch immer noch im ASCII-Code dargestellt und nicht verstümmelt ist. Sie müssen compress der Komprimierungsbibliothek aufrufen. Nach der tatsächlichen Komprimierung beträgt die Lautstärke etwa 1/3 der vorherigen, was sehr beeindruckend ist.
Zusammenfassung
Es ist etwas schwierig, globale Variablen importierbar zu halten. Die Frage, der ich mich stellen muss, ist: Wenn ich die Dinge, die ich heute eingelegt habe, in Zukunft öffnen muss, kann ich sie dann trotzdem öffnen?
Hier gibt es mehrere Versionen: Projektversion, Python-Version, Pickle-Protokollversion und Paketversionen, von denen das Projekt abhängt. Unter diesen denke ich, dass die Python-Version und die Pickle-Version sicher auf ihre Abwärtskompatibilität vertrauen können und leicht zu lösen sind. Hauptsächlich das Projekt und die Version sowie abhängige Versionen. Wenn das auszuwählende Objekt sehr komplex ist, ist die Sicherung der alten Version wahrscheinlich nicht mit der neuen Version kompatibel. Eine mögliche Lösung besteht darin, alle Abhängigkeiten vollständig zu sperren, beispielsweise deren Hashwerte aufzuzeichnen. Wenn Sie eine bestimmte Binärsequenz wiederherstellen möchten, stellen Sie die spezifischen Abhängigkeiten und spezifischen Commits des Projekts zu diesem Zeitpunkt wieder her.
Aber im Moment besteht unsere Anforderung grundsätzlich darin, ein „requests.Response“-Objekt auszuwählen. Ich denke, wir können uns auf ihre Abwärtskompatibilität verlassen. Wenn es eines Tages zu einer bahnbrechenden Änderung der Anforderungen kommt, ist der Code selbst dann nicht kompatibel, wenn er kompatibel ist. Zu diesem Zeitpunkt können andere Strategien in Betracht gezogen werden.
Das obige ist der detaillierte Inhalt vonObjektserialisierung und Deserialisierung der in Python integrierten Pickle-Bibliothek. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!