Serializarea și deserializarea obiectelor Python Partea 2

Aceasta este partea a doua a unui tutorial despre serializarea și deserializarea obiectelor Python. În prima parte, ai învățat elementele de bază și apoi te-ai ascuns în insulele din Pickle și JSON. 

În această parte veți explora YAML (asigurați-vă că aveți exemplul de funcționare din partea unu), discutați considerațiile privind performanța și securitatea, obțineți o recenzie a formatelor de serializare suplimentare și în sfârșit aflați cum să alegeți schema potrivită.

YAML

YAML este formatul meu preferat. Este un format de serializare a datelor pentru oameni. Spre deosebire de Pickle și JSON, aceasta nu face parte din biblioteca standard Python, deci trebuie să o instalați:

pip instalare yaml

Modulul yaml are numai sarcină() și benă () funcții. În mod implicit funcționează cu șiruri de caractere sarcini () și halde (), dar poate lua un al doilea argument, care este un flux deschis și apoi poate arunca / încărca în / din fișiere.

import yaml print yaml.dump (simplu) boolean: true int_list: [1, 2, 3] nici unul: null număr: 3.44 text: șir

Observați cât de ușor poate fi citit YAML cu Pickle sau chiar cu JSON. Și acum pentru partea cea mai tare despre YAML: înțelege obiectele Python! Nu este nevoie de encodere personalizate și de decodoare. Aici este serializarea / deserializarea complexă folosind YAML:

> serializat = yaml.dump (complex)> print serializat a: !! python / object: __ main __.O simplu: boolean: true int_list: [1, 2, 3] 03-07 00:00:00> deserialized = yaml.load (serializat)> deserialized == complex Adevărat

După cum puteți vedea, YAML are notația proprie pentru a marca obiectele Python. Rezultatul este încă ușor de citit de om. Obiectul datetime nu necesită nici o etichetare specială deoarece YAML suportă în mod inerent obiecte datetime. 

Performanţă

Înainte de a începe să vă gândiți la performanță, trebuie să vă gândiți dacă performanța este o preocupare deloc. Dacă serializați / deserializați o cantitate mică de date relativ rar (de ex. Citirea unui fișier de configurare la începutul unui program), atunci performanța nu este într-adevăr o preocupare și puteți trece mai departe.

Dar, presupunând că ați profilat sistemul dvs. și ați descoperit că serializarea și / sau deserializarea cauzează probleme de performanță, aici sunt lucrurile pe care trebuie să le adresați.

Sunt două aspecte ale performanței: cât de repede puteți serializa / deserializa și cât de mare este reprezentarea serializată?

Pentru a testa performanța diferitelor formate de serializare, voi crea o structură de date extinsă și o va serializa / deserializa folosind Pickle, YAML și JSON. Date mare lista cuprinde 5.000 de obiecte complexe.

big_data = [dict (a = simplă, atunci când = datetime.now () înlocuim (microsecond = 0)) pentru i în intervalul (5000)]

murătură

Voi folosi IPython aici pentru că este convenabil % timeit magie care măsoară timpul de execuție.

import cPickle ca murătură În [190]:% timeit serialized = pickle.dumps (big_data) 10 bucle, cel mai bun de 3: 51 ms per bucla În [191]:% timeit deserialized = pickle.loads 3: 24,2 ms pe buclă În [192]: deserializat == big_data Out [192]: True In [193]: len (serializat) Out [193]: 747328

Murătura implicită durează 83,1 milisecunde pentru serializare și 29,2 milisecunde pentru a deserializa, iar dimensiunea serializată este de 747328 octeți.

Să încercăm cu cel mai înalt protocol.

În [195]:% timeit serialized = pickle.dumps (big_data, protocol = pickle.HIGHEST_PROTOCOL) 10 bucle, cel mai bine de 3: 21,2 ms per bucla În [196]:% timeit deserialized = cel mai bun de 3: 25,2 ms pe buclă În [197]: len (serializat) Out [197]: 394350

Rezultate interesante. Timpul de serializare a scăzut la numai 21,2 milisecunde, dar timpul de deserializare a crescut puțin până la 25,2 milisecunde. Dimensiunea serializată s-a redus semnificativ la 394,350 octeți (52%).

JSON

În [253]% timeit serialized = json.dumps (big_data, cls = CustomEncoder) 10 bucle, cel mai bun de 3: 34,7 ms pe buclă În [253]% timeit deserialized = json.loads (serialized, object_hook = decode_object) cel mai bun de 3: 148 ms pe buclă În [255]: len (serializat) Out [255]: 730000

O.K. Performanța pare a fi ceva mai proastă decât Pickle pentru codificare, dar mult, mult mai rău pentru decodare: de 6 ori mai lent. Ce se întâmplă? Acesta este un artefact al object_hook care trebuie să ruleze pentru fiecare dicționar pentru a verifica dacă trebuie să îl convertească la un obiect. Rularea fără cârlig obiect este mult mai rapidă.

% timeit deserialized = json.loads (serializat) 10 bucle, cel mai bun de 3: 36,2 ms pe buclă

Lectia de aici este ca atunci cand serializati si deserializati la JSON, luati in considerare foarte atent orice codificare personalizata, deoarece acestea pot avea un impact major asupra performantei globale.

YAML

În [294]:% timeit serialized = yaml.dump (big_data) 1 bucle, cel mai bun de 3: 1.22 s pe buclă În [294]:% timeit deserialized = yaml.load (serializat) 1 bucle, per bucla În [295]: len (serializat) Out [295]: 200091

O.K. YAML este într-adevăr, foarte lent. Dar, notați ceva interesant: dimensiunea serializată este de numai 200.091 octeți. Mult mai bine decât Pickle și JSON. Să privim într-adevăr rapid:

În [300]: imprimat serializat [: 211] - a: & id001 boolean: adevărat int_list: [1, 2, 3] nici unul: null număr: 3,44 text: șir când: 2016-03-13 00:11:44 - : * id001 când: 2016-03-13 00:11:44 - a: * id001 când: 2016-03-13 00:11:44

YAML este foarte inteligent aici. Acesta a identificat că toate cele 5.000 de dictionare au aceeași valoare pentru cheia "a", așa că o stochează o singură dată și o referă la ea * id001 pentru toate obiectele.

Securitate

Securitatea este o preocupare critică adesea. Pickle și YAML, în virtutea construirii obiectelor Python, sunt vulnerabile la atacurile de execuție a codului. Un fișier formatat inteligent poate conține cod arbitrar care va fi executat de Pickle sau YAML. Nu este nevoie să fiți alarmați. Acesta este conceput și documentat în documentația lui Pickle:

Atenție: Modulul de decapare nu este destinat să fie protejat împotriva datelor eronate sau construite în mod greșit. Nu deconectați niciodată datele primite de la o sursă neautentificată sau neautentificată.

Ca și în documentația YAML:

Atenție: nu este sigur să apelați yaml.load cu orice date primite de la o sursă de încredere! yaml.load este la fel de puternic ca și pickle.load și poate apela orice funcție Python.

Trebuie doar să înțelegeți că nu trebuie să încărcați date serializate primite din surse de încredere utilizând Pickle sau YAML. JSON este OK, dar din nou dacă aveți codificatoare personale / decodoare personalizate decât să fiți expuși.

Modulul yaml furnizează yaml.safe_load () funcție care va încărca doar obiecte simple, dar atunci veți pierde o mulțime de putere YAML și poate opta pentru a utiliza doar JSON.

Alte formate

Există multe alte formate de serializare disponibile. Iată câteva dintre ele.

Protobuf

Protobuf sau bufferele de protocol reprezintă formatul de schimb al datelor Google. Este implementat în C ++, dar are legături Python. Dispune de o schemă sofisticată și împachetează eficient datele. Foarte puternic, dar nu foarte ușor de utilizat.

MessagePack

MessagePack este un alt format popular de serializare. Este, de asemenea, binar și eficient, dar spre deosebire de Protobuf nu necesită o schemă. Are un sistem tip care este similar cu JSON, dar un pic mai bogat. Cheile pot fi de orice tip și nu sunt acceptate doar șiruri de caractere și șiruri de caractere non-UTF8.

CBOR

CBOR reprezintă reprezentarea concisă a obiectului binar. Din nou, acceptă modelul de date JSON. CBOR nu este la fel de bine cunoscut ca Protobuf sau MessagePack, dar este interesant din două motive: 

  1. Este un standard oficial pentru Internet: RFC 7049.
  2. Acesta a fost conceput special pentru Internetul obiectelor (IoT).

Cum de a alege?

Aceasta este întrebarea mare. Cu atât de multe opțiuni, cum alegi? Să luăm în considerare diferiții factori care ar trebui luați în considerare:

  1. Formatul serializat ar trebui să poată fi citit de om și / sau editat de om?
  2. Conținutul serializat va fi primit de la surse nedovedite?
  3. Este serializarea / deserializarea un obstacol al performanței?
  4. Datele serializate trebuie schimbate cu medii non-Python?

Voi face foarte ușor pentru dvs. și va acoperi mai multe scenarii comune și formatul pe care îl recomand pentru fiecare:

Salvarea automată a stării locale a unui program Python

Utilizați pickle (cPickle) aici cu HIGHEST_PROTOCOL. Este rapid, eficient și poate stoca și încărca cele mai multe obiecte Python fără nici un cod special. Poate fi folosit și ca cache persistent local.

Fișiere de configurare

Categoric YAML. Nimic nu-și bate simplitatea pentru ceea ce oamenii trebuie să citească sau să editeze. Este folosit cu succes de către Anhaps și multe alte proiecte. În unele situații, puteți prefera să utilizați module drept Python ca fișiere de configurare. Aceasta poate fi alegerea corectă, dar atunci nu este serializare, și este într-adevăr o parte a programului și nu un fișier de configurare separat.

API-uri web

JSON este câștigătorul clar aici. Aceste zile, API-urile Web sunt consumate cel mai adesea de aplicațiile web JavaScript care vorbesc nativ JSON. Unele API-uri Web pot întoarce alte formate (de exemplu, CSV pentru seturi de rezultate dense cu tabel), dar aș susține că puteți pachete de date csv în JSON cu cheltuieli minime (nu este nevoie să repetați fiecare rând ca un obiect cu toate numele coloanelor). 

Comunicare pe scară largă cu volum mare / low-latență

Utilizați unul dintre protocoalele binare: Protobuf (dacă aveți nevoie de o schemă), MessagePack sau CBOR. Rulați-vă propriile teste pentru a verifica performanța și puterea reprezentativă a fiecărei opțiuni.

Concluzie

Serializarea și deserializarea obiectelor Python este un aspect important al sistemelor distribuite. Nu puteți trimite obiecte Python direct peste fir. De multe ori aveți nevoie să interopeați cu alte sisteme implementate în alte limbi și, uneori, doriți doar să stocați starea programului în depozitare persistentă. 

Python vine cu mai multe scheme de serializare în biblioteca standard și multe altele sunt disponibile ca module terțe. Fiind conștient de toate opțiunile și avantajele și dezavantajele fiecăruia vă va permite să alegeți cea mai bună metodă pentru situația dvs..

Cod