7. Intrări și ieșiri
********************

Există mai multe moduri de a prezenta *rezultatul* (sau *ieșirea*, de
la englezescul *output*) execuției unui program; datele conținute în
acest rezultat pot fi afișate într-o formă ușor de citit de către
utilizatorii umani (în englezește, ca jargon, *human-readable*) sau
pot fi scrise într-un fișier pentru o întrebuințare ulterioară. În
capitolul de față vom discuta despre câteva din aceste feluri de
prezentare.


7.1. Formatări elegante ale ieșirii
===================================

Până acum am întâlnit două modalități de scriere a valorilor:
formulările de *expresii* și funcția "print()". (O a treia modalitate
este să folosim metoda "write()" a obiectelor fișier; fișierul
standard de ieșire poate fi accesat ca "sys.stdout". Vedeți Referința
bibliotecii pentru mai multe informații în această privință.)

Adesea, veți dori să aveți mai mult control asupra formatului datelor
de afișat decât simpla posibilitate de a le tipări cu câte un spațiu
gol între ele. Dispunem de diferite procedee de a formata ieșirile.

* Pentru a folosi șiruri de caractere literale formatate, începeți
  șirul cu "f" sau cu "F", poziționate înaintea ghilimelelor duble ori
  a celor triple de deschidere. În interiorul unui asemenea șir de
  caractere, puteți scrie o expresie Python încadrată de caracterele
  "{" și "}", care să se refere la variabile ori la date literale.

     >>> anul = 2016
     >>> evenimentul = 'referendumul'
     >>> f'Rezultate la {evenimentul} din {anul}.'
     'Rezultate la referendumul din 2016.'

* Metoda "str.format()" a șirurilor de caractere necesită puțin mai
  multă muncă de redactare. Și aici veți utiliza "{" și "}" la
  încadrarea locului în care va fi substituită o anumită variabilă,
  respectiv veți avea posibilitatea de a da indicații detaliate în
  privința formatării, doar că va trebui să oferiți și informația de
  formatat. În blocul de cod care urmează se află două exemple despre
  cum se formatează variabilele:

     >>> voturi_pentru = 42_572_654
     >>> voturi_in_total = 85_705_149
     >>> procentaj = voturi_pentru / voturi_in_total
     >>> '{:-9} voturi PENTRU  {:2.2%}'.format(voturi_pentru, procentaj)
     ' 42572654 voturi PENTRU  49.67%'

  Să remarcăm că valoarea lui "voturi_pentru" este precedată de un
  spațiu gol la numerele pozitive și de un semn minus la cele
  negative. În același timp, codul din exemplu va afișa valoarea lui
  "procentaj" înmulțită cu 100 și urmată de două zecimale, respectiv
  de semnul procent (a se vedea Mini-limbajul specificației de format
  pentru detalii).

* În sfârșit, puteți să vă ocupați chiar dumneavoastră de întreaga
  manipulare a șirurilor de caractere folosind operațiile de tranșare
  și de concatenare a șirurilor pentru a crea orice machetă (de la
  englezescul *layout*) de afișare vă doriți. Tipul de date șir de
  caractere posedă diverse metode de spațiere a șirurilor la lățimi de
  caracter date.

Atunci când nu vă interesează nicio ieșire sofisticată ci, pur și
simplu, vreți să afișați rapid valorile câtorva variabile în scopul
depanării programului la care lucrați, ați putea converti orice
valoare obținută într-un șir de caractere cu ajutorul funcțiilor
"repr()" și "str()".

Funcția "str()" a fost gândită să returneze reprezentări de valori
care să fie ușor de citit de către utilizatorii umani, pe când funcția
"repr()" a primit rolul de a genera reprezentări de date care să fie
citite de către interpretorul de Python (sau să ridice o excepție
"SyntaxError" atunci când reprezentările nu se potrivesc niciunui
șablon sintactic). În cazul obiectelor care nu posedă nicio
reprezentare distinctivă de oferit unui consumator uman, "str()" va
întoarce aceeași valoare ca și "repr()". Multe valori, cum ar fi
numerele ori structurile de date de tipul listelor și al
dicționarelor, primesc aceeași reprezentare de la amândouă funcțiile.
Șirurile de caractere, pe de altă parte, posedă două reprezentări
diferite.

Câteva exemple:

   >>> s = 'Salutare, lume.'
   >>> str(s)
   'Salutare, lume.'
   >>> repr(s)
   "'Salutare, lume.'"
   >>> str(1/7)
   '0.14285714285714285'
   >>> x = 10 * 3.25
   >>> y = 200 * 200
   >>> s = 'Valoarea lui x este ' + repr(x) + ', iar a lui y este ' + repr(y) + '...'
   >>> print(s)
   Valoarea lui x is 32.5, iar a lui y este 40000...
   >>> # Aplicarea lui repr() unui șir îi adaugă acestuia ghilimele simple
   >>> # și linii oblice inverse:
   >>> salut = 'salutare, lume\n'
   >>> salutări = repr(salut)
   >>> print(salutări)
   'salutare, lume\n'
   >>> # Argumentul lui repr() poate fi orice obiect Python:
   >>> repr((x, y, ('carne presată', 'ouă')))
   "(32.5, 40000, ('carne presată', 'ouă'))"

Modulul "string" conține o clasă "Template" care oferă încă o metodă
de substituire a valorilor în șirurile de caractere, pe baza unor
substitute de forma "$x" ce vor fi înlocuite cu valori preluate dintr-
un dicționar, însă care furnizează mult mai puțin control asupra
formatării.


7.1.1. Șiruri de caractere literale formatate
---------------------------------------------

Șirurile de caractere literale formatate (supranumite, pe scurt,
*f-șiruri*) ne permit să introducem valoarea unei expresii Python în
interiorul unui șir de caractere prefixând șirul cu "f" sau "F" și
scriind expresia în cauză drept "{expresia}".

Un specificator de format opțional îi poate urma expresiei. Prezența
lui oferă un control sporit asupra modulului în care valoarea
expresiei este formatată. Exemplul dat în continuare prezintă
rotunjirea lui pi la trei cifre după virgulă (punct):

   >>> import math
   >>> print(f'Valoarea lui pi este de aproximativ {math.pi:.3f}.')
   Valoarea lui pi este de aproximativ 3.142.

Inserarea unui număr întreg imediat după semnul "':'" va avea ca efect
printarea unui câmp lat de minim tot atâta caractere cât este valoarea
inserată. O atare proprietate se dovedește utilă la alinierea
coloanelor într-o afișare.

   >>> tabel = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
   >>> for nume, telefon in tabel.items():
   ...     print(f'{nume:10} ==> {telefon:10d}')
   ...
   Sjoerd     ==>       4127
   Jack       ==>       4098
   Dcab       ==>       7678

Alți modificatori pot fi întrebuințați la conversia unei valori
înainte de formatare. Astfel, "'!a'" îi aplică valorii funcția
"ascii()", "'!s'" o aplică pe "str()" iar "'!r'" pe "repr()":

   >>> animale = 'țipari'
   >>> print(f'Barca mea pe pernă de aer e plină de {animale}.')
   Barca mea pe pernă de aer e plină de țipari.
   >>> print(f'Barca mea pe pernă de aer e plină de {animale!r}.')
   Barca mea pe pernă de aer e plină de 'țipari'.

Specificatorul "=" poate fi utilizat la extinderea unei expresii sub
formă de text al expresiei, urmat de semnul egal și de reprezentarea
evaluării expresiei respective:

>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

A se vedea expresii auto-documentate pentru mai multe informații
privind specificatorul "=". Pentru o descriere cuprinzătoare a tuturor
acestor specificații de format, consultați ghidul de referință despre
Mini-limbajul specificației de format.


7.1.2. Metoda format() a tipului șir de caractere
-------------------------------------------------

Utilizarea de bază a metodei "str.format()" se face astfel -- apelând,
ca de obicei, la o scenetă Monty Python:

   >>> print('Noi suntem {} care strigă "{}!"'.format('bravii', 'Bau'))
   Noi suntem bravii care strigă "Bau!"

Acoladele și caracterele încadrate de ele (care se numesc *câmpuri de
format*) vor fi înlocuite de obiectele transmise metodei
"str.format()". Un număr trecut între acolade poate fi utilizat ca
referire la poziția obiectului ce trebuie transmis metodei
"str.format()".

   >>> print('{0} și {1}'.format('șuncă presată', 'ouă'))
   șuncă presată și ouă
   >>> print('{1} și {0}'.format('șuncă presată', 'ouă'))
   ouă și șuncă presată

Dacă vom întrebuința argumente cuvânt-cheie la apelul metodei
"str.format()", atunci ne vom referi la valorile acestora prin
intermediul numelor argumentelor respective.

   >>> print('Această {aliment} este {nume_predicativ}.'.format(
   ...       aliment='șuncă presată', nume_predicativ='absolut îngrozitoare'))
   Această șuncă presată este absolut îngrozitoare.

Argumentele poziționale și cele cuvânt-cheie pot fi combinate în mod
arbitrar:

   >>> print('Povestea lui {0}, {1} și {altul}.'.format('Bill', 'Manfred',
   ...                                                    altul='Georg'))
   Povestea lui Bill, Manfred și Georg.

În caz că aveți de manevrat un șir de format realmente lung și țineți
neapărat să nu-l partiționați, ar fi util dacă v-ați putea referi la
variabilele care trebuie formatate nu cu numărul -- indicele poziției
-- ci cu numele. O atare rezolvare se obține transmițând, pur și
simplu, un dicționar de date și folosind parantezele pătrate "'[]'"
pentru a-i accesa cheile.

   >>> tabel = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
   ...       'Dcab: {0[Dcab]:d}'.format(tabel))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Același rezultat poate fi obținut transmițând dicționarul de date
"tabel" ca pe un argument cuvânt-cheie dat în notația "**".

   >>> tabel = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
   >>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**tabel))
   Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Procedeul este extrem de folositor în combinație cu funcția
predefinită "vars()", care returnează un dicționar de date cu toate
variabilele locale:

   >>> tabel = {k: str(v) for k, v in vars().items()}
   >>> mesaj = " ".join([f'{k}: ' + '{' + k +'};' for k in tabel.keys()])
   >>> print(mesaj.format(**tabel))
   __name__: __main__; __doc__: None; __package__: None; __loader__: ...

Ca exemplu, liniile de cod următoare produc un set de coloane aliniate
compact care afișează numere întregi împreună cu pătratele și cuburile
lor:

   >>> for x in range(1, 11):
   ...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

Pentru o dare de seamă elaborată asupra formatării șirurilor de
caractere folosind "str.format()", a se vedea Sintaxa șirurilor de
format.


7.1.3. Formatare manuală a șirurilor de caractere
-------------------------------------------------

Iată același tabel de pătrate și cuburi de numere întregi, însă
formatat manual:

   >>> for x in range(1, 11):
   ...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
   ...     # Remarcați folosirea lui 'end' pe linia anterioară
   ...     print(repr(x*x*x).rjust(4))
   ...
    1   1    1
    2   4    8
    3   9   27
    4  16   64
    5  25  125
    6  36  216
    7  49  343
    8  64  512
    9  81  729
   10 100 1000

(Luați în considerare faptul că un spațiu gol, între două coloane
consecutive, i se datorează modului de funcționare al funcției
"print()": aceasta adaugă întotdeauna spații goale între argumentele
sale.)

Metoda "str.rjust()" a obiectelor șir de caractere aliniază *la
dreapta* un șir într-un câmp de lățime dată inserând spații goale în
stânga șirului. Python-ul dispune de încă două metode similare, și
anume "str.ljust()", respectiv "str.center()". Ele nu inserează nimic
(în vechiul șir), doar întorc un șir nou. Dacă șirul ce trebuie
manipulat este prea lung, aceste metode nu-l vor trunchia, ci îl vor
returna nemodificat; deși un asemenea comportament vă va afecta dpdv.
estetic macheta coloanelor, alternativa ar fi mult mai rea, ea
presupunând modificări de valori. (Dacă doriți, cu adevărat, o
trunchiere a șirului manipulat, atunci puteți apela la operațiile de
tranșare, cum ar fi "x.ljust(n)[:n]".)

Mai există o metodă, "str.zfill()", care completează *la stânga*, cu
zerouri, șirurile de numere. Are capacitatea de a distinge cifrele de
semnele plus și minus:

   >>> '12'.zfill(5)
   '00012'
   >>> '-3.14'.zfill(7)
   '-003.14'
   >>> '3.14159265359'.zfill(5)
   '3.14159265359'


7.1.4. Vechiul stil de formatare al șirurilor de caractere
----------------------------------------------------------

Operatorul % (modulo) poate fi întrebuințat și la formatarea șirurilor
de caractere. Fiind dată expresia "formatul % valorile" (unde
*formatul* desemnează un șir de caractere), specificațiile conversiei
"%" care sunt scrise în *formatul* vor fi înlocuite cu zero sau mai
multe elemente din *valorile*. Această operație este cunoscută sub
numele de *interpolarea șirurilor* (de caractere). De exemplu:

   >>> import math
   >>> print('Valoarea lui pi este aproximativ %5.3f.' % math.pi)
   Valoarea lui pi este aproximativ 3.142.

Mai multe informații se găsesc în secțiunea Formatarea șirurilor de
caractere în vechiul stil printf.


7.2. Citirea și scrierea fișierelor
===================================

"open()" returnează un *obiect fișier* și este utilizată de obicei cu
două argumente poziționale și un argument cuvânt-cheie:
"open(nume_de_fișier, mod_de_deschidere, encoding=None)"

   >>> f = open('fișier_șantier', 'w', encoding="utf-8")

Primul argument este un șir de caractere desemnând numele fișierului
pe care dorim să-l deschidem. Cel de-al doilea argument este tot un
șir de caractere, foarte scurt, ce descrie felul în care vom folosi
fișierul. *mod_de_deschidere* va fi "'r'" dacă dorim doar să citim
fișierul, "'w'" dacă ne interesează numai să scriem în el (în caz că
există fișierul omonim, acesta va fi șters), respectiv "'a'" pentru
adăugare la sfârșitul fișierului. "'r+'" va deschide fișierul atât
pentru citit cât și pentru scris. Argumentul *mod_de_deschidere* este
opțional; dacă nu-l precizăm, atunci el va fi considerat "'r'".

De obicei, fișierele se deschid în *modul text*, ceea ce înseamnă că
puteți citi șiruri de caractere din ele, respectiv scrie șiruri de
caractere în ele, șiruri care sunt codificate (de la englezescul
*encoding*) conform unei *codificări* anumite. Atunci când
*codificarea* nu este precizată, codificarea implicită va depinde de
platforma de calcul (vedeți "open()"). Dat fiind că UTF-8 constituie,
în zilele noastre, standardul *de facto* în domeniu, este recomandat
să folosiți "encoding="utf-8"" dacă nu aveți, realmente, nevoie de
vreo codificare specială. Adăugând sufixul "'b'" la modul de
deschidere, fișierul în cauză va fi deschis în *modul binar*. Datele
accesate în modul binar sunt citite și scrise sub formă de obiecte
"octeți". Nu vi se permite să specificați care este *codificarea* dacă
deschideți fișierul în modul binar.

Atunci când manevrăm fișierul în modul text, la citire din el,
caracterele care marchează sfârșitul de rând se vor transforma
implicit din caractere sfârșit de rând *specifice* platformei de
calcul (adică, din "\n" în Unix, respectiv din "\r\n" în Windows) în
caracterul "\n". Invers, la scrierea în fișier, toate aparițiile lui
"\n" vor deveni implicit caracterele de sfârșit de rând specifice
platformei pe care lucrăm. Deși o astfel de modificare *sub capotă*
(de la englezescul *behind-the-scenes*) este convenabilă atunci când
avem de a face cu fișiere text, ea va corupe datele binare precum cele
dintr-un fișier "JPEG" ori "EXE". Aveți mare grijă să folosiți modul
binar atunci când fie citiți din asemenea fișiere fie scrieți în ele.

O bună practică ne este la îndemână prin intermediul cuvântului-cheie
"with", de întrebuințat dacă manevrăm obiecte fișier. Avantajul său
constă în aceea că fișierul va fi întotdeauna închis corect la finalul
execuției expresiei acestui construct, și aceasta chiar dacă a fost
ridicată vreo excepție pe parcurs. În plus, folosirea lui "with"
scurtează semnificativ codul care ar trebui scris dacă folosim
constructul echivalent dat de blocurile "try"-"finally":

   >>> with open('fișier_șantier', encoding="utf-8") as f:
   ...     citește_datele = f.read()

   >>> # Putem verifica dacă fișierul a fost închis automat.
   >>> f.closed
   True

În caz că nu utilizați cuvântul cheie "with", atunci va trebui să
apelați metoda "f.close()" pentru a închide fișierul și a elibera de
îndată eventualele resurse ale sistemului pe care manevrarea
fișierului le-a blocat.

Atenționare:

  Apelul lui "f.write()" fie fără să folosim cuvântul cheie "with" fie
  fără să-l însoțim de apelul lui "f.close()" *poate* avea drept
  consecință o scriere incompletă a argumentelor lui "f.write()" pe
  disc, și aceasta chiar dacă programul se va încheia corect.

După ce un obiect fișier a fost închis, fie printr-o instrucție "with"
fie prin apelul metodei "f.close()", orice încercare de a-l
întrebuința va fi sortită eșecului.

   >>> f.close()
   >>> f.read()
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ValueError: I/O operation on closed file.


7.2.1. Metodele obiectelor fișier
---------------------------------

Restul exemplelor din această secțiune presupune că un obiect fișier
pe nume "f" a fost deja creat.

Ca să citiți conținutul unui fișier, apelați "f.read(cantitate)",
instrucțiune care citește o anumită cantitate de date și o returnează
sub formă de șir de caractere (în modul text) sau sub forma unui
obiect *bytes* (în modul binar). *cantitate* este un argument numeric
opțional. Atunci când *cantitate* fie este omis fie este număr
negativ, întregul conținut al fișierului va fi citit și apoi returnat;
treaba dumneavoastră dacă fișierul în cauză se dovedește a fi de două
ori mai mare ca memoria mașinii de calcul. Altfel, cel mult un număr
de *cantitate* de caractere (în modul text) vor fi citite și
returnate, respectiv un număr de *cantitate* de biți (în modul binar)
vor fi citiți și returnați. Atunci când se va ajunge la finalul
fișierului, "f.read()" va întoarce un șir gol ("''").

   >>> f.read()
   'Acesta este întregul fișier.\n'
   >>> f.read()
   ''

"f.readline()" citește un singur rând din fișier; caracterul linie-
nouă ("\n") este păstrat la sfârșitul șirului de caractere citit, el
fiind omis numai la ultimul rând al fișierului, atunci când fișierul
nu se încheie cu o *linie nouă* (în englezește, ca jargon, *newline*).
Procedeul în cauză face ca valoarea returnată să fie lipsită de
ambiguitate; căci, dacă "f.readline()" returnează un șir gol înseamnă
că am ajuns la finalul fișierului, în timp ce o linie goală (dintre
paragrafe) va fi reprezentată de un "'\n'", adică de un șir conținând
numai un singur caracter, cel de linie nouă.

   >>> f.readline()
   'Acesta este primul rând al fișierului.\n'
   >>> f.readline()
   'Al doilea rând al fișierului\n'
   >>> f.readline()
   ''

Pentru a citi mai multe linii din fișier, puteți itera de-a lungul
obiectului fișier. O atare procedură este eficientă dpdv. al
consumului de memorie, este rapidă și se bazează pe un cod Python
simplu:

   >>> for rând in f:
   ...     print(rând, end='')
   ...
   Acesta este primul rând al fișierului.
   Al doilea rând al fișierului

Dacă vă interesează să plasați toate rândurile unui fișier într-o
listă, atunci ați putea folosi "list(f)" sau "f.readlines()".

"f.write(șir_de_caractere)" scrie conținutul lui *șir_de_caractere* în
fișier, returnând numărul de caractere scrise.

   >>> f.write('Acesta e un test\n')
   17

Obiectele de alt tip trebuie convertite mai întâi -- fie la șiruri de
caractere (în modul text) fie la obiecte *bytes* (în modul binar) --
înainte de scrierea în fișier:

   >>> valoarea = ('răspunsul', 42)
   >>> s = str(valoarea)  # convertește tuplul în șir de caractere
   >>> f.write(s)
   17

"f.tell()" returnează un număr întreg care fie indică poziția curentă
în obiectul fișier precizând la câți octeți ne găsim față de începutul
fișierului, atunci când fișierul a fost deschis în modul binar, fie
este o valoare *opacă*, atunci când fișierul a fost deschis în modul
text.

Pentru a schimba poziția (cursorului) în obiectul fișier,
întrebuințați metoda "f.seek(deplasament, sursă)". Poziția se
calculează adunând acest *deplasament* la punctul de referință;
punctul de referință este precizat prin argumentul *sursă*. Valoarea 0
a lui *sursă* înseamnă că ne vom raporta la începutul fișierului,
valoarea 1 se referă la poziția curentă în fișier, iar valoarea 2
implică folosirea finalului de fișier pe post de punct de referință.

   >>> f = open('fișier_șantier', 'rb+')
   >>> f.write(b'0123456789abcdef')
   16
   >>> f.seek(5)      # Mergi la cel de-al 6-lea octet din fișier
   5
   >>> f.read(1)
   b'5'
   >>>                # Mergi la octetul situat cu 3 poziții (octeți)
   >>>                # înaintea finalului
   >>> f.seek(-3, 2)
   13
   >>> f.read(1)
   b'd'

În fișierele text (adică, fișierele deschise fără să se utilizeze
niciun "b" în șirul de caractere al modului de deschidere) sunt
permise doar poziționările raportate la începutul fișierului (singura
excepție fiind cea dată de poziționarea, folosind "seek(0, 2)", chiar
la finalul fișierului), iar valorile corecte pentru *deplasament* sunt
numerele returnate de "f.tell()", respectiv zero. Orice alt
*deplasament* va cauza comportamente impredictibile.

Fișierele obiect posedă diverse alte metode, precum "isatty()" și
"truncate()" care sunt mai rar întâlnite; consultați Referința
bibliotecii pentru ghidul cuprinzător al obiectelor fișier.


7.2.2. Salvarea structurilor de date cu "json"
----------------------------------------------

Șirurile de caractere se scriu ușor într-un fișier și se citesc (la
fel de) ușor dintr-un fișier. Numerele presupun un efort suplimentar,
dat fiind că metoda "read()" returnează doar șiruri de caractere, iar
acestea din urmă trebuie transmise unei funcții precum "int()", care
funcție preia un șir ca "'123'" și întoarce valoarea sa numerică,
adică pe 123. Atunci când vă interesează să salvați date de diverse
tipuri mai sofisticate decât șirurile ori numerele, cum ar fi listele
imbricate ori dicționarele de date, *parsarea* manuală și
*serializarea* de mână devin niște sarcini complicate.

Ca să nu-i silească pe utilizatori să-și consume timpul scriind și
corectând cod specializat în salvarea în fișiere a tipurilor
complicate de date, Python-ul le permite acestora să întrebuințeze
popularul format de (inter)schimb de date numit JSON (Notația
obiectelor JavaScript). Modulul predefinit "json" preia date Python
ierarhizate și le convertește în reprezentări sub formă de șiruri de
caractere; un atare proces este numit *serializare*. Invers,
reconstrucția datelor din reprezentările lor ca șiruri de caractere
poartă numele de *deserializare*. Între o serializare și
deserializarea subsecventă, șirul de caractere care reprezintă un
obiect Python poate fi stocat într-un fișier sau într-un flux de date,
ori transmis printr-o conexiune la rețeaua Internet către o mașină de
calcul aflată la distanță.

Notă:

  Formatul JSON este utilizat frecvent de aplicațiile moderne care
  permit schimburi de date. Mulți programatori sunt deja familiarizați
  cu el, ceea ce îl face o alegere potrivită atunci când ne
  interesează interoperabilitatea.

Dacă aveți la îndemână un obiect Python "x", atunci vă ajunge o
singură linie de cod ca să-i vedeți reprezentarea JSON sub formă de
șir de caractere:

   >>> import json
   >>> x = [1, 'banală', 'listă']
   >>> json.dumps(x)
   '[1, "banală", "listă"]'

O variantă a funcției "dumps()", intitulată "dump()", serializează
de-a dreptul obiectul Python într-un *fișier text*. Așadar, dacă "f"
este un obiect *fișier text* deschis pentru scriere, putem proceda
astfel:

   json.dump(x, f)

Ca redecodificăm obiectul, presupunând că "f" este fie un obiect
*fișier binar* fie un obiect *fișier text* care a fost deja deschis
pentru citire, executăm:

   x = json.load(f)

Notă:

  Fișierele JSON trebuie să fie codificate UTF-8. Folosiți
  "encoding="utf-8"" atunci când deschideți un fișier JSON ca *fișier
  text* atât pentru citire cât și pentru scriere.

O asemenea tehnică simpl(ist)ă de serializare se descurcă la
manevrarea listelor și a dicționarelor de date, însă serializarea în
JSON a unor instanțe de clase oarecare necesită un anume efort.
Referința la modulul "json" din documentație explică de ce.

Vezi și:

  "pickle" - modulul pickle

  Spre deosebire de JSON, *pickle* (adică, *muratul* sau *punerea la
  murat*) desemnează un protocol capabil să serializeze obiecte Python
  oricât de complicate. Din care motiv, îi este tipic (doar) Python-
  ului și nu poate fi utilizat la comunicarea cu aplicații scrise în
  alte limbaje de programare. De asemeni, este *nesigur* în mod
  prestabilit: deserializarea unor date puse la murat pe care le-ați
  obținut de la o sursă dubioasă poate provoca execuții de cod despre
  care (să) nu știți nimic, în caz că datele au fost preparate de un
  atacator îndemânatic.
