Dies ist Teil zwei eines Tutorials zum Serialisieren und Deserialisieren von Python-Objekten. Im ersten Teil haben Sie die Grundlagen gelernt und sich dann mit den Besonderheiten von Pickle und JSON beschäftigt.
In diesem Teil lernen Sie YAML (stellen Sie sicher, dass Sie ein laufendes Beispiel aus Teil 1 haben), diskutieren Sie Leistungs- und Sicherheitsaspekte, erhalten Sie einen Überblick über weitere Serialisierungsformate und erfahren Sie, wie Sie das richtige Schema auswählen.
YAML ist mein Lieblingsformat. Es ist ein menschenfreundliches Datenserialisierungsformat. Im Gegensatz zu Pickle und JSON ist es nicht Teil der Python-Standardbibliothek. Sie müssen es also installieren:
pip install yaml
Das yaml-Modul hat nur Belastung()
und dump ()
Funktionen. Standardmäßig arbeiten sie mit Strings wie Ladungen()
und Dumps ()
, kann jedoch ein zweites Argument annehmen, das ein offener Stream ist und dann in / aus Dateien abladen / laden kann.
import yaml print yaml.dump (einfach) boolean: true int_list: [1, 2, 3] none: Nullzahl: 3.44 Text: Zeichenfolge
Beachten Sie, wie lesbar YAML mit Pickle oder sogar JSON verglichen wird. Und jetzt zum coolsten Teil über YAML: Es versteht Python-Objekte! Keine benutzerdefinierten Encoder und Decoder erforderlich. Hier ist die komplexe Serialisierung / Deserialisierung mit YAML:
> serialized = yaml.dump (complex)> print serialized a: !! python / object: __ main __ Ein einfaches: boolean: true int_list: [1, 2, 3] none: Nullzahl: 3.44 Text: String bei: 2016- 03-07 00:00:00> deserialized = yaml.load (serialized)> deserialized == complex True
Wie Sie sehen, hat YAML eine eigene Notation, um Python-Objekte zu kennzeichnen. Die Ausgabe ist immer noch sehr gut lesbar. Das datetime-Objekt erfordert kein spezielles Tagging, da YAML von datetime-Objekten inhärent unterstützt wird.
Bevor Sie über Leistung nachdenken, müssen Sie erst darüber nachdenken, ob Leistung überhaupt ein Problem ist. Wenn Sie eine kleine Datenmenge relativ selten serialisieren / deserialisieren (z. B. Lesen einer Konfigurationsdatei am Anfang eines Programms), ist die Leistung nicht wirklich ein Problem und Sie können weitermachen.
Wenn Sie jedoch davon ausgehen, dass Sie Ihr System profiliert haben und festgestellt haben, dass Serialisierung und / oder Deserialisierung zu Leistungsproblemen führen, sollten Sie Folgendes beachten.
Dies sind zwei Aspekte für die Leistung: Wie schnell können Sie serialisieren / deserialisieren und wie groß ist die serialisierte Darstellung?
Um die Leistung der verschiedenen Serialisierungsformate zu testen, erstelle ich eine umfangreiche Datenstruktur und serialisiert / deserialisiert sie mit Pickle, YAML und JSON. Das Große Daten
Die Liste enthält 5.000 komplexe Objekte.
big_data = [dict (a = einfach, wenn = datetime.now (). replace (Mikrosekunde = 0)) für i im Bereich (5000)]
Ich werde IPython hier für seine Bequemlichkeit verwenden % timeit
magische Funktion, die Ausführungszeiten misst.
cPickle als Pickle importieren In [190]:% timeit serialized = pickle.dumps (big_data) 10 Schleifen, am besten von 3: 51 ms pro Schleife In [191]:% timeit deserialized = pickle.loads (serialisiert) 10 Schleifen, best of 3: 24,2 ms pro Schleife In [192]: deserialized == big_data Out [192]: True In [193]: len (serialisiert) Out [193]: 747328
Die Standardeinstellung für die Serialisierung beträgt 83,1 Millisekunden und die Deserialisierung 29,2 Millisekunden. Die serialisierte Größe beträgt 747.328 Byte.
Versuchen wir es mit dem höchsten Protokoll.
In [195]:% timeit serialized = pickle.dumps (big_data, protocol = pickle.HIGHEST_PROTOCOL) 10 Schleifen, am besten von 3: 21,2 ms pro Schleife In [196]:% timeit deserialized = pickle.loads (serialisiert) 10 Schleifen, Best of 3: 25,2 ms pro Schleife In [197]: len (serialisiert) Out [197]: 394350
Interessante Ergebnisse. Die Serialisierungszeit verringerte sich auf nur 21,2 Millisekunden, die Deserialisierungszeit erhöhte sich jedoch leicht auf 25,2 Millisekunden. Die serialisierte Größe schrumpfte signifikant auf 394.350 Bytes (52%)..
In [253]% timeit serialized = json.dumps (big_data, cls = CustomEncoder) 10 Schleifen, am besten von 3: 34,7 ms pro Schleife In [253]% timeit deserialized = json.loads (serialisiert, object_hook = decode_object) 10 Schleifen. Best of 3: 148 ms pro Schleife In [255]: len (serialisiert) Out [255]: 730000
OK. Die Performance scheint beim Codieren etwas schlechter als bei Pickle zu sein, beim Decodieren jedoch viel, viel schlechter: 6-mal langsamer. Was ist los? Dies ist ein Artefakt der object_hook
Funktion, die für jedes Wörterbuch ausgeführt werden muss, um zu prüfen, ob es in ein Objekt konvertiert werden muss. Das Laufen ohne Objekthaken ist viel schneller.
% timeit deserialized = json.loads (serialized) 10 Schleifen, am besten von 3: 36,2 ms pro Schleife
Die Lektion hier ist, dass bei der Serialisierung und Deserialisierung in JSON alle benutzerdefinierten Kodierungen sorgfältig geprüft werden sollten, da sie die Gesamtleistung erheblich beeinflussen können.
In [293]:% timeit serialized = yaml.dump (big_data) 1 Schleifen, am besten von 3: 1,22 s pro Schleife In [294]:% timeit deserialized = yaml.load (serialisiert) 1 Schleifen, am besten 3: 2,03 s pro Schleife In [295]: len (serialisiert) Out [295]: 200091
OK. YAML ist wirklich sehr langsam. Beachten Sie jedoch etwas Interessantes: Die serialisierte Größe beträgt nur 200.091 Bytes. Viel besser als sowohl Pickle als auch JSON. Schauen wir uns schnell mal rein:
In [300]: print serialized [: 211] - a: & id001 boolean: true int_list: [1, 2, 3] none: Nullzahl: 3.44 Text: Zeichenfolge, wenn: 2016-03-13 00:11:44 - a : * id001 wann: 2016-03-13 00:11:44 - a: * id001 wann: 2016-03-13 00:11:44
YAML ist hier sehr klug. Es hat festgestellt, dass alle 5.000 Diktaturen den gleichen Wert für den 'a'-Schlüssel haben, also nur einmal gespeichert und unter Verwendung referenziert werden * id001
für alle Objekte.
Sicherheit ist oft ein kritisches Anliegen. Pickle und YAML sind durch das Erstellen von Python-Objekten anfällig für Codeausführungsangriffe. Eine intelligent formatierte Datei kann beliebigen Code enthalten, der von Pickle oder YAML ausgeführt wird. Es besteht keine Notwendigkeit, alarmiert zu werden. Dies ist von Entwurf und ist in der Dokumentation von Pickle dokumentiert:
Warnung: Das Pickle-Modul soll nicht vor fehlerhaften oder in böswilliger Absicht erstellten Daten geschützt sein. Entpacken Sie niemals Daten aus einer nicht vertrauenswürdigen oder nicht authentifizierten Quelle.
Sowie in der YAML-Dokumentation:
Warnung: Es ist nicht sicher, yaml.load mit Daten aus einer nicht vertrauenswürdigen Quelle anzurufen! yaml.load ist so leistungsfähig wie pickle.load und kann daher jede Python-Funktion aufrufen.
Sie müssen nur wissen, dass Sie keine serialisierten Daten laden dürfen, die von nicht vertrauenswürdigen Quellen mit Pickle oder YAML empfangen wurden. JSON ist in Ordnung, aber auch wenn Sie benutzerdefinierte Encoder / Decoder haben, können Sie auch ausgesetzt sein.
Das yaml-Modul liefert die yaml.safe_load ()
Funktion, die nur einfache Objekte lädt, aber dann verlieren Sie eine Menge an YAMLs Leistung und entscheiden sich möglicherweise für die Verwendung von JSON.
Es gibt viele andere Serialisierungsformate. Hier sind einige davon.
Protobuf oder Protokollpuffer ist das Datenaustauschformat von Google. Es ist in C ++ implementiert, verfügt jedoch über Python-Bindungen. Es verfügt über ein ausgefeiltes Schema und packt Daten effizient. Sehr leistungsfähig, aber nicht sehr einfach zu bedienen.
MessagePack ist ein weiteres beliebtes Serialisierungsformat. Es ist auch binär und effizient, aber im Gegensatz zu Protobuf ist kein Schema erforderlich. Es hat ein Typensystem, das JSON ähnelt, aber etwas reicher ist. Schlüssel können von jedem Typ sein. Es werden nicht nur Strings und Nicht-UTF8-Strings unterstützt.
CBOR steht für Concise Binary Object Representation. Wieder unterstützt es das JSON-Datenmodell. CBOR ist nicht so bekannt wie Protobuf oder MessagePack, ist aber aus zwei Gründen interessant:
Das ist die große Frage. Wie wählen Sie bei so vielen Optionen aus? Betrachten wir die verschiedenen Faktoren, die berücksichtigt werden sollten:
Ich werde es Ihnen sehr leicht machen und einige gängige Szenarien behandeln und welches Format ich für jedes Szenario empfehle:
Verwenden Sie hier Pickle (cPickle) mit dem HIGHEST_PROTOCOL
. Es ist schnell, effizient und kann die meisten Python-Objekte ohne besonderen Code speichern und laden. Es kann auch als lokaler permanenter Cache verwendet werden.
Auf jeden Fall YAML. Nichts ist einfacher als alles, was Menschen zum Lesen oder Bearbeiten benötigen. Es wird erfolgreich von Ansible und vielen anderen Projekten verwendet. In einigen Situationen ziehen Sie es vor, gerade Python-Module als Konfigurationsdateien zu verwenden. Dies kann die richtige Wahl sein, aber dann ist es keine Serialisierung, und es ist wirklich Teil des Programms und keine separate Konfigurationsdatei.
JSON ist hier der klare Gewinner. Heutzutage werden Web-APIs am häufigsten von JavaScript-Webanwendungen verwendet, die nativ JSON sprechen. Einige Web-APIs geben möglicherweise andere Formate zurück (z. B. csv für dichte tabellarische Ergebnismengen). Ich würde jedoch argumentieren, dass Sie csv-Daten mit minimalem Aufwand in JSON packen können (Sie müssen nicht jede Zeile als Objekt mit allen Spaltennamen wiederholen)..
Verwenden Sie eines der Binärprotokolle: Protobuf (wenn Sie ein Schema benötigen), MessagePack oder CBOR. Führen Sie Ihre eigenen Tests durch, um die Leistung und die repräsentative Leistungsfähigkeit jeder Option zu überprüfen.
Die Serialisierung und Deserialisierung von Python-Objekten ist ein wichtiger Aspekt verteilter Systeme. Sie können Python-Objekte nicht direkt über die Leitung senden. Sie müssen häufig mit anderen Systemen zusammenarbeiten, die in anderen Sprachen implementiert sind, und manchmal möchten Sie nur den Status Ihres Programms in einem permanenten Speicher speichern.
Python verfügt über mehrere Serialisierungsschemata in seiner Standardbibliothek. Viele weitere sind als Module von Drittanbietern verfügbar. Wenn Sie alle Möglichkeiten und Vor- und Nachteile kennen, können Sie die für Ihre Situation beste Methode wählen.