Serializarea și deserializarea obiectelor Python este un aspect important al oricărui program non-trivial. Dacă în Python salvați ceva într-un fișier, dacă citiți un fișier de configurare sau dacă răspundeți la o solicitare HTTP, faceți obiectul de serializare și de deserializare.
Într-un sens, serializarea și deserializarea sunt cele mai plictisitoare lucruri din lume. Cui îi pasă de toate formatele și protocoalele? Vrei doar să persești sau să creezi niște obiecte Python și să le recuperezi mai târziu intacte.
Acesta este un mod foarte sănătos de a privi lumea la nivel conceptual. Dar, la nivel pragmatic, ce schemă de serializare, formatul sau protocolul pe care îl alegeți poate determina cât de repede se execută programul dvs., cât de sigur este, cât de multă libertate trebuie să vă mențineți statul și cât de bine veți interoga cu alte sisteme.
Motivul pentru care există atât de multe opțiuni este că circumstanțele diferite necesită soluții diferite. Nu există "o singură mărime pentru toate". În acest tutorial în două părți voi trece peste avantajele și dezavantajele celor mai reușite programe de serializare și deserializare, arăta cum să le folosească și să ofere orientări pentru a alege între ele atunci când se confruntă cu un caz de utilizare specific.
În secțiunile următoare voi serializa și deserializa aceleași grafice de obiecte Python folosind serialele diferite. Pentru a evita repetarea, voi defini aceste grafuri obiect aici.
Graficul simplu al obiectului este un dicționar care conține o listă de numere întregi, un șir, un float, un modul boolean și o Nr.
simple = dict (int_list = [1, 2, 3], text = "șir", număr = 3.44, boolean =
Graficul complex de obiecte este, de asemenea, un dicționar, dar conține a datetime
obiect și instanță de clasă definită de utilizator care are a self.simple
atribut, care este setat la graficul obiect simplu.
de la datetime import datetime clasa A (obiect): def __init __ (auto, simplu): self.simple = simplu def __eq __ (auto, altul): daca nu hasattr (altul, simplu): returnare False return self.simple == alt.simple def __ne __ (sine, altul): dacă nu hasattr (altul, 'simplu'): întoarcere True întoarcere self.simple! = other.simple complex = dict (a = A (simplu) 3, 7))
Pickle este un discontinue. Este un format de serializare a obiectului Python nativ. Interfața pickle oferă patru metode: dump, dumps, load și loads. benă ()
metoda se serializează într-un fișier deschis (obiect ca fișier). halde ()
metoda se serializează la un șir. sarcină()
metoda deserializează dintr-un obiect deschis ca un fișier. sarcini ()
metoda deserializează dintr-un șir.
Pickle acceptă în mod implicit un protocol textual, dar are și un protocol binar, care este mai eficient, dar nu poate fi citit de om (util atunci când se depanează).
Iată cum măriți un graf de obiecte Python într-un șir și într-un fișier utilizând ambele protocoale.
import cPickle ca pickle.dumps pickle.dumps (simplu) "(dp1 \ nS'text '\ np2 \ nS'string' \ np3 \ nsS'none '\ np4 \ nNsS'boolean \ np5 \ nI01 \ nsSnumber \ np6 \ nF3.4399999999999999 \ nsS'int_list '\ NP7 \ n (lp8 \ Ni1 \ naI2 \ naI3 \ nas.“pickle.dumps (simplu, protocol = pickle.HIGHEST_PROTOCOL)„\ x80 \ X02 q \ x01 (U \ x04textq \ x02U \ x06stringq \ x03U \ x04noneq \ x04NU \ x07boolean \ x88U \ x06numberq \ x05G @ \ x0b \ X85 \ x1e \ xb8Q \ xeb \ x85U \ x08int_list] q \ X06 (K \ x01K \ x02K \ x03eu.“
Reprezentarea binară poate părea mai mare, dar aceasta este o iluzie datorită prezentării sale. Când se dă un fișier, protocolul textual este de 130 octeți, în timp ce protocolul binar este de numai 85 de octeți.
pickle.dump (simplu, deschis ('simple1.pkl', 'w')) pickle.dump (simplu, deschis ('simple2.pkl', 'wb'), protocol = pickle.HIGHEST_PROTOCOL) ls -la sim *. * -rw-r - r-- 1 gigi personal 130 Mar 9 02:42 simple1.pkl -rw-r - r-- 1 gigi personal 85 Mar 9 02:43 simple2.pkl
Decuparea dintr-un șir este la fel de simplă:
x = pickle.loads ( "(DP1 \ nS'text '\ NP2 \ nS'string' \ NP3 \ nsS'none '\ PN4 \ nNsS'boolean' \ np5 \ nI01 \ nsS'number '\ np6 \ nF3.4399999999999999 \ nsSint_list '\ np7 \ n (lp8 \ nI1 \ naI2 \ naI3 \ nas ") afirma x == simplu x = pickle.loads (' \ x80 \ x02 q \ x01 (U \ x04textq \ x02U \ x06stringq \ x03U \ x04noneq \ x04NU \ x07boolean \ x88U \ x06numberq \ x05G @ \ x0b \ X85 \ x1e \ xb8Q \ xeb \ x85U \ x08int_list] q \ X06 (K \ x01K \ x02K \ x03eu. ') afirma x == simplu
Rețineți că pickle-ul poate descoperi protocolul în mod automat. Nu este nevoie să specificați un protocol chiar și pentru cel binar.
Decuplarea dintr-un fișier este la fel de ușoară. Trebuie doar să oferiți un fișier deschis.
x = pickle.load (deschide ('simple1.pkl')) assert x == simplu x = pickle.load (deschide ('simple2.pkl')) assert x == simple x = .pkl ',' rb ')) afirmă x == simplu
Conform documentației, ar trebui să deschideți murăturile binare utilizând modul "rb", dar după cum puteți vedea că funcționează fie în felul.
Să vedem cum se ocupă marina cu graficul complex al obiectelor.
pickle.dumps (complex) „(DP1 \ nS'a '\ nccopy_reg \ n_reconstructor \ NP2 \ n (c__main __ \ nc__builtin __ \ NObject \ PN4 \ nNtRp5 \ n (DP6 \ nS'simple nA \ NP3 \' \ NP7 \ n ( dp8 \ nS'text '\ np9 \ nS'string' \ NP10 \ nsS'none '\ np11 \ nNsS'boolean' \ np12 \ nI01 \ nsS'number '\ np13 \ nF3.4399999999999999 \ nsS'int_list' \ np14 \ n (lp15 \ Ni1 \ naI2 \ naI3 \ nassbsS'when '\ np16 \ ncdatetime \ ndatetime \ np17 \ n (S' \\ X07 \\ \\ xe0 x03 \\ X07 \\ \\ x00 x00 \\ x00 \\ x \\ x00 \\ x00 '\ ntRp18 \ ns. "pickle.dumps (complex, protocol = pickle.HIGHEST_PROTOCOL) \ x80 \ x02 q \ x01 q \ x04U \ x06simpleq \ X05 q \ X06 (U \ x04textq \ x07U \ x06stringq \ x08U \ x04noneq \ tNU \ x07boolean \ x88U \ x06numberq \ nG @ \ x0b \ X85 \ x1e \ xb8Q \ xeb \ x85U \ x08int_list] q \ x0b (K \ x01K \ x02K \ x03eusbU \ x04whenq \ x0ccdatetime \ ndatetime \ nq \ rU \ n \ X07 \ xe0 \ x03 \ X07 \ x00 \ x00 \ x00 \ x00 \ x00 \ x00 \ x85Rq \ x0eu.“decapant .dump (complex, open ('complex1.pkl', 'w')) pickle.dump (complex, open ('complex2.pkl', 'wb'), protocol = pickle.HIGHEST_PROTOCOL) -rw-r-r-- 1 gigi personal 327 Mar 9 02:58 complex1.pkl -rw-r-r-- 1 gigi personal 171 Mar 9 02:58 complex2.pkl
Eficiența protocolului binar este chiar mai mare cu grafice complexe de obiecte.
JSON (JavaScript Object Notation) face parte din biblioteca standard Python de la Python 2.5. O să consider un format nativ în acest moment. Este un format bazat pe text și este regele neoficial al web-ului în ceea ce privește serializarea obiectului. Sistemul său de tip modelează în mod natural JavaScript, deci este destul de limitat.
Să serializăm și să deserializăm graficele obiectelor simple și complexe și să vedem ce se întâmplă. Interfața este aproape identică cu interfața pickle. Tu ai benă ()
, halde ()
, sarcină()
, și sarcini ()
funcții. Dar, nu există protocoale de selectat și există multe argumente opționale pentru a controla procesul. Să începem să simplificăm prin eliminarea graficului obiect simplu fără argumente speciale:
importul json print json.dumps (simplu) "text": "string", "none": null, "boolean": true, "number": 3,44, "int_list": [1, 2, 3]
Rezultatul arată destul de ușor de citit, dar nu există nici o indentare. Pentru un grafic mai mare de obiecte, aceasta poate fi o problemă. Să dați linia de ieșire:
jon.dumps (simplu, liniuță = 4) "text": "string", "none": null, "boolean": true, "number": 3,44; int_list: [1, 2, 3]
Arată mult mai bine. Să trecem la graficul complex al obiectelor.
json.dumps (complex) -------------------------------------------- ------------------------------- Trasarea tipului de eroare (ultimul apel ultimul)în () ----> 1 json.dumps (complex) /usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.pyc în haldele (kw) 241 cls este Niciuna, iar indentarea este Niciuna, iar separatoarele sunt Nici una, iar codarea 242 == 'utf-8' și implicit este Nici unul și nu sort_keys și nu kw): -> 243 return _default_encoder.encode (obj) 244 dacă cls este Nici unul: 245 cls = JSONEncoder /usr/local/Cellar/python/2.7.10/Frameworks/Python.framework /Versions/2.7/lib/python2.7/json/encoder.pyc în encode (self, o) 205 # excepții nu sunt la fel de detaliate. Apelul în listă ar trebui să fie de aproximativ 206 # echivalent cu PySequence_Fast pe care o va face ".join ()" -> 207 chunks = auto.iterencode (o, _one_shot = True) 208 dacă nu este o instanță (bucăți, listă, tuplu) : 209 bucăți = listă (bucăți) /usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc în iterencode (self, o, _one_shot ) 268 self.key_separator, self.item_separator, self.sort_keys, 269 self.skipkeys, _one_shot) -> 270 _iterencode retur (o, 0) 271 272 def _make_iterencode (markeri, _default, _encoder, _indent, _floatstr, / usr / local / Cellar / python / 2.7.10 / Frameworks / Python.framework / Versiuni / 2.7 / lib / python2.7 / json / encoder.pyc implicit (auto, o) 182 183 " repr (o) + "nu este JSON serializabil") 185 186 def encode (self, o): TypeError: <__main__.A object at 0x10f367cd0> nu este JSON serializabil
Uau! Asta nu arată prea bine. Ce s-a întâmplat? Mesajul de eroare este că obiectul A nu este serializabil JSON. Amintiți-vă că JSON are un sistem de tip foarte limitat și că nu poate sincroniza automat clasele definite de utilizator. Modul de abordare este de a subclasa clasa JSONEncoder folosită de modulul json și de a implementa Mod implicit()
care se numește ori de câte ori codificatorul JSON rulează într-un obiect pe care nu îl poate serializa.
Funcția encoderului personalizat este de ao transforma într-un grafic de obiecte Python pe care codificatorul JSON este capabil să îl codifice. În acest caz, avem două obiecte care necesită codificare specială: datetime
obiect și clasa A. Următorul codificator face lucrarea. Fiecare obiect special este convertit în a dict
în cazul în care cheia este numele de tipul înconjurat de dunders (dublu sublinieri). Acest lucru va fi important pentru decodificare.
din datetime import datetime import json class CustomEncoder (json.JSONEncoder): def implicit (self, o): ifinstance (o, datetime): retur '__datetime__': o.replace (microsecond = 0) .isoformat __ __ "format (o .__ clasa __.__ nume__): o .__ dict__
Să încercăm din nou codificatorul nostru personalizat:
serializat = json.dumps (complex, indent = 4, cls = CustomEncoder) imprimat serializat "a": "__A__": "simple": " ": adevărat," număr ": 3.44," int_list ": [1, 2, 3]," când ": " __datetime__ ":" 2016-03-07T00: 00: 00 "
Acest lucru este frumos. Graficul complex al obiectelor a fost serializat corespunzător, iar informația de tip originală a componentelor a fost reținută prin intermediul tastelor: "__A__" și "__datetime__". Dacă folosiți numele pentru numele dvs., atunci trebuie să veniți cu o convenție diferită pentru a denumi tipuri speciale.
Să decodifice graficul complex al obiectelor.
> deserialized = json.loads (serializate)> deserialized == complex Fals
Hmmm, deserializarea a funcționat (fără erori), dar este diferită de graful obiect complex complex pe care l-am serializat. Ceva este greșit. Să aruncăm o privire la graficul obiectului deserializat. O să folosesc pprint
funcția pprint
modul pentru imprimarea drăguță.
> de la pprint import pprint> pprint (deserializat) u'a ': u' __ A__ ': u'simple': u'boolean ': Adevărat u'int_list': [1, 2, 3] 'none': Nici unul, u'number ': 3.44, u'text': u'string ', u'when': u '__ datetime__': u'2016-03-07T00: 00: 00 '
O.K. Problema este că modulul json nu știe nimic despre clasa A sau chiar despre obișnuitul datetime. El doar deserializează totul în mod implicit la obiectul Python care se potrivește cu sistemul de tip. Pentru a vă întoarce la un grafic bogat de obiecte Python, aveți nevoie de decodare personalizată.
Nu este nevoie de o subclasă de decodor personalizată. sarcină()
și sarcini ()
funcțiile furnizează parametrul "object_hook" care vă permite să furnizați o funcție personalizată care convertește dicts în obiecte.
Definiți o valoare de tip decode_object (o): dacă '__A__' în o: a = A () a .__ dict __.date (o ['__ A__']) return datetime.strptime (o ['__ datetime__' ], '% Y-% m-% dT% H:% M:% S') retur o
Să decodăm folosind decode_object ()
funcția ca parametru pentru sarcini ()
parametrul object_hook.
> deserialized = json.loads (serializat, object_hook = decode_object)> imprimare deserializată u'a ': <__main__.A object at 0x10d984790>, u'when ': datetime.datetime (2016, 3, 7, 0, 0)> deserializat == complex Adevărat
În prima parte a acestui tutorial, ați învățat despre conceptul general de serializare și deserializare a obiectelor Python și ați explorat insultele și obiectele Python în serializarea folosind obiectele Pickle și JSON.
În partea a doua, veți afla despre preocupările YAML, despre performanță și securitate și despre o revizuire rapidă a schemelor suplimentare de serializare.
Aflați Python cu ghidul nostru complet de instrucțiuni Python, indiferent dacă sunteți doar începători sau sunteți un coder experimentat în căutarea unor noi abilități.