Sunteți pe pagina 1din 30

Lucrarea 1.

Programarea în limbajul Python – structuri de date


În această lucrare veţi învăţa:

 Care sun particularitățile programării în limbajul Python


 Cum să instalați Python

? 


Cum să utilizați stucturile complexe de date
Cum să realizați operații pe liste, tuple și dicționare
Cum să generați liste

Cuvinte cheie: Python, particularități, stucturi de date, operații

Cuprins
Lucrarea 1. Programarea în limbajul Python – structuri de date .....................................................1
1.1 Particularități sintactice ....................................................................................................2
1.1.1 Delimitarea blocurilor ..................................................................................................2
1.1.2 Structurile For şi Case ...................................................................................................2
1.2 Instalare Python ................................................................................................................4
1.3 Structuri complexe de date ...............................................................................................7
1.4 Exerciții cu liste și stringuri................................................................................................9
1.5 Exerciții cu tuple ............................................................................................................. 12
1.6 Exerciții cu dicționare ..................................................................................................... 13
1.7 Operații care funcționează similar pe liste, tuple și dicționare ..................................... 14
1.8 Atribuirea şi copierea structurilor de date..................................................................... 19
1.9 Stive, cozi, mulțimi, imagini de dicționar ....................................................................... 23
1.10 Listele generate .............................................................................................................. 26
1.11 Bibliografie ..................................................................................................................... 30
Particularități sintactice 2

1.1 Particularități sintactice


1.1.1 Delimitarea blocurilor
Python este un limbaj multiparadigmă (obiectual, funcțional, scriptural, aspect-oriented etc.). O
caracteristică sintactică importantă este lipsa delimitatorilor de blocuri. În majoritatea limbajelor
blocurile de instrucțiuni se delimitează cu acolade sau perechi de instrucțiuni (FOR…ENDFOR,
BEGIN…END etc.). În locul acestora, Python folosește indentarea. Exemplu:

for x in colectie:
comenzile blocului

sau

if a==0:
primul bloc
else:
bloc alternativ

Blocurile se indentează spre dreapta. La finalul blocului, se șterge indentarea revenind cu


cursorul spre stânga. Aceasta face sintaxa mai ușor de citit, evitând construcții suplimentare din alte
limbaje:

for ….
{
comenzi
}

sau

for i=1 to n
comenzi
endfor

Pe de altă parte apar unele riscuri: sunt mai dificil de detectat cu ochiul liber blocurile încadrate
eronat, în special când se fac indentări multiple (blocuri în blocuri), iar neglijența poate face ca o
instrucțiune să fie indentată cu un spațiu mai la stânga sau mai la dreapta, ceea ce poate afecta logica
programului!

1.1.2 Structurile For şi Case

În Python nu există instrucţiunea Case, iar For nu acceptă forma For i=0 to n.
Pentru ciclurile for e acceptată doar forma:

for i in colectie

Pentru a implementa un For contorizat cu i de la 0 la n, se foloseşte:

for i in range(0,n+1)
3 Lucrarea 1. Programarea în limbajul Python – structuri de date

Funcţia range() returnează lista valorilor dintre cele două argumente, în exemplul de faţă de la 0
până la n (al doilea argument al lui range() nu intră, de aceea al doilea argument e mai mare cu 1 decât
limita dorită!). Mai mult, ciclurile For şi While pot avea ramura else:

for i in colectie:
instructiuni
else:
instructiuni

while conditie:
instructiuni
else:
instructiuni

Ramura Else se va executa imediat după finalizarea ciclului. Se poate argumenta că oricum
instrucţiunea care urmează după For şi While va fi executată la finalizarea ciclului. Există totuşi o situaţie
în care Else face diferenţa: dacă ciclul For sau While conţine o instrucţiune de ieşire forţată din ciclu
(Break), aceasta va sări şi peste ramura Else! Pentru Case, se foloseşte un If cu mai multe ramuri:

if conditie:
instructiuni
elif conditie:
instructiuni
elif conditie:
instructiuni
………………..
else:
instructiuni

E evident că Else corespunde în acest caz ramurii Otherwise/Default din alte limbaje (cazul în
care nici una din condiţii nu e îndeplinită) iar Elif corespunde unei ramuri Case normale. E important de
reţinut că dacă mai multe condiţii sunt adevărate în acelaşi timp doar ramura primeia va fi executată.
Deci nu există mecanismul din alte limbaje conform căruia la finalul fiecărei ramuri Case să fie nevoie de
o instrucţiune de ieşire forţată din structura Case.
Instalare Python 4

1.2 Instalare Python


Pas1. Se downloadează pachetul Python 2.7 pentru Windows:

http://www.python.org/download/

Observație: S-a lansat recent versiunea Python 3, dar sunt unele probleme de compatibilitate cu librăriile
și extensiile care nu au migrat încă la noua versiune/sintaxă, de aceea e preferabil a nu se lucra cu cea
mai recentă versiune.

Pas2. Se instalează pachetul Python 2.7. Componentele instalate și accesibile din Start-Programs
sunt:
 IDLE – este un prompter (linie de comandă) și un editor de cod sursă optimizat pentru sintaxa
Python;
 Python Command Line – un prompter cu linie de comandă de tip Windows (de obicei e
preferabilă folosirea lui IDLE);
 Module Docs – motor de căutare pentru documentațiile bibliotecilor instalate; de exemplu, prin
tastarea "json" se obține lista funcțiilor pentru procesare JSON (componente ale modului JSON);
 Python Manuals – Sistemul Help (inclusiv Tutoriale și Exemple).

Pas3. Selectarea directorului de lucru. Acesta este folderul din care interpretorul Python va
accesa fișiere în mod direct, fără să fie necesară explicitarea căilor.
În mod implicit, folderele de lucru sunt rădăcina Python27 și Python27\Lib, dar se recomandă
crearea unui folder propriu, de exemplu: C:\Python27\Exemple (e riscant să se lucreze pe folderele
implicite, care conțin o serie de fișiere de bază pentru funcționarea limbajului și a bibliotecilor implicite).
3.a.Se creează directorul de lucru C:\Python27\Exemple
3.b.Se memorează acest folder în variabila de mediu PYTHONPATH, în felul următor:
 în My Computer – Properties – Advanced – Environment Variables
 această rubrică listează variabilele de mediu setate pentru Windows
 cu butonul New (din System Variables) se creează variabila PYTHONPATH cu valoarea
C:\Python27\Exemple (pe unele sisteme poate necesita restartare)
 de la acest moment, fișierele Python trebuie salvate în acest folder.

Pas4. Instalarea managerului de pachete. Acesta este util pentru a descărca și instala cu efort
minim module externe, care nu sunt disponibile în pachetul Python de bază. Instalarea sa este opțională,
putând fi făcută ulterior, atunci când e nevoie de un pachet extern.
Există multiple metode de a instala module de tip package (biblioteci de funcții orientate pe
probleme specifice):
 Unele dintre acestea au site-uri proprii pe care sunt oferite executabile ce asigură toate
configurările necesare;
 Unele nu sunt oferite în formă executabilă, ci în format sursă, accesibile și printr-un index pe site-
ul oficial al limbajului:

http://pypi.python.org/pypi?%3Aaction=index

(sau pot fi căutate cu caseta Search a site-ului). Acestea sunt oferite ca arhivă ZIP ce conține un fișierul
setup.py ce trebuie executat pentru a realiza instalarea pachetului, cu următoarea comandă la
5 Lucrarea 1. Programarea în limbajul Python – structuri de date

prompterul Windows (execuția comenzii necesită să ne aflăm în folderul pachetului, unde se află fișierul
setup.py, iar calea fișierului python.exe să fie recunoscută de Windows - memorată în variabila de mediu
PATH - sau tastată complet):

python setup.py install

 Metoda cea mai facilă este instalarea managerului de pachete setuptools de la adresa
http://pypi.python.org/pypi/setuptools

(versiunea Windows Installer pt. Python 2.7 în cazul nostru); acest manager permite folosirea comenzii
easy_install care se ocupă și de căutarea bibliotecii dorite pe Internet, și de descărcarea versiunii dorite,
și de instalare; un tutorial cu opțiunile de folosire ale easy_install e disponibil la adresa

http://peak.telecommunity.com/DevCenter/EasyInstall

După ce managerul setuptools e instalat, comanda easy_install se poate executa în linia de


comandă Windows (Command Prompt) și recomandat e să fie memorată inclusiv în variabila de mediu
PATH pentru a putea fi executată cu efort minim (altfel se poate executa doar din folderul
C:\Python27\Scripts. După instalare, în Command Prompt se poate executa

easy_install –h

pentru a obține lista opțiunilor cu care se poate controla instalarea pachetelor.

Observație: Opțional se poate instala și un mediu de programare mai avansat (Spyder, PyDev,
PIDA). O listă cu mediile de programare Python e accesibilă la

http://wiki.python.org/moin/IntegratedDevelopmentEnvironments

Pas5. Verificarea instalării. Se lansează mediul de lucru IDLE, apare prompterul:

>>>
Se tastează o atribuire:
>>> a=2
Se afișează valoarea variabilei a:
>>> a
2
În felul acesta, la linia de comandă se pot exersa în timpul învățării diverse funcții și operații
Python.
Din mediul de lucru IDLE se selectează File-New Window. Apare o fereastră nouă, fără linie de
comandă. Aceasta poate fi folosită pentru a scrie fișiere Python (cu extensia py). Fișierele Python nu sunt
neapărat programe. Ele pot conține inițializări de constante, definiții de clase sau funcții ce pot fi ulterior
importate și reutilizate (practic, acestea sunt bibliotecile create de utilizator).
Termenii folosiți pentru biblioteci sunt:
 module (modul), pt. fișierele .py create de utilizator,
 package (pachete), pt. bibliotecile de tip extensie (instalabile cu comanda easy_install).
Instalare Python 6

Ambele tipuri de biblioteci trebuie importate în Python înainte de a fi utilizate. În continuare


vom crea un modul. În fereastra nou deschisă în IDLE se tastează:

a=2
def f1(x):
return x+x
note={'Info':{'Ion':2,'Maria':5,'Ana':10},'Mate':{'Ion':6,'Maria':3,'Ana':9}}

Se observă că pe linia 3, după tastarea caracterului ":" urmat de Enter, rândul se indentează
AUTOMAT pentru a indica începerea blocului funcției. Indentarea automată e asigurată de IDLE (și alte
medii de programare Python), dar dacă se folosesc editoare de texte simple (Notepad) indentarea
trebuie făcută manual. La finalizarea blocului funcției (linia 4), se revine la indentarea inițială în mod
manual.
Elementele tastate sunt:
 o atribuire (variabila a inițializată cu 2);
 o funcție f1 cu un argument, x: se observă că funcțiile Python se definesc cu def, iar valoarea
returnată se indică în mod clasic, cu return;
 o atribuire, de data aceasta prin inițializarea unui vector asociativ (un dicționar de dicționare).
Se salvează fișierul în Python27/Exemple, cu numele ex.py. Se revine la prompterul IDLE:
>>> import ex
- comanda importă modulul ex.py (dacă folderul Exemple a fost memorat în variabila de mediu
PYTHONPATH, după cum am indicat anterior) și creează un spațiu de nume care va califica orice variabilă
sau funcție din modul. Spre exemplu, pt. a accesa variabilele și funcția din modul se tastează la
prompter:
>>> ex.a
2
>>> ex.note
{'Info':{'Ion':2,'Maria':5,'Ana':10},'Mate':{'Ion':6,'Maria':3,'Ana':9}}
>>> ex.f1(2)
4
Dacă se fac modificări la modul (fișier), acesta trebuie reimportat! Adăugați la ex.py o linie nouă:
b=3
După salvare, pentru a putea accesa noua variabilă, la prompter trebuie să se tasteze:
>>> reload(ex)
>>> ex.b
3
Dacă se dorește evitarea spațiului de nume și a calificării, importul se va face astfel:
>>> from ex import *
>>> a
2
>>> f1(3)
6
Fiecare variabilă, funcție, clasă din modul poate fi importată separat:
>>> from ex import f1
>>> a
- eroare (variabila a nu a fost importată)
>>> f1(4)
8
(funcția f1 a fost importată)
7 Lucrarea 1. Programarea în limbajul Python – structuri de date

1.3 Structuri complexe de date


În Python vectorii asociativi se numesc dicționare și pot fi descriși printr-o sintaxă similară cu
JSON - virgula separă perechile nume:valoare, acoladele încadrează elementele unui dicționar.
Dicționarul este o structură de date complexă întâlnită în majoritatea limbajelor sub diverse nume: hash
(Ruby), vector asociativ (PHP), obiect (JavaScript)). Datorită versatilității sale, dicționarul poate stoca
obiecte, tabele, matrici eterogene, arbori etc. În cazul de față (variabila note din modulul ex.py)
stochează două "înregistrări". Spre deosebire de alte limbaje, dicționarele Python permit ca eticheta-
cheie (prima parte dintr-o pereche nume-valoare) să fie și de alte tipuri decât string. Exemple:

a='Ion'
b='Maria'
c={a:4,b:5}
devine echivalent cu
c={'Ion':4,'Maria':5}

- în acest caz, cheile sunt valori de variabile;


d={4:'Ion',5:'Ana'}

– aici cheile sunt numere! funcționează ca un vector clasic căruia îi lipsesc unii indici (d*4+,d*5+ au valori,
dar d[0],d[1] nu!)
e={('Ion','Andrei','Maria'):4, ('Alin','Andreea'):10}

- aici cheile sunt tuple (înregistrări)

În schimb nu sunt permise cheile de tip dicționar sau listă. Următoarele două exemple sunt
eronate:
g={{'Ion':3,'Andrei':5}:'Info',{'Maria':3,'Alin':2}:'Mate'}
h={[Ion,Andrei]:3,[Maria,Alin]:4}

Pe lângă dicționar, Python folosește două structuri de date vectoriale asemănătoare:


 lista (șir de valori încadrat între paranteze pătrate);
 tuplul (șir de valori încadrat între paranteze rotunde).
Care este diferența?
 lista e gândită pentru a exprima o succesiune de valori de importanță similară;
 tuplul e gândit pentru a exprima o alăturare de valori eterogene ce alcătuiesc un întreg, similare
înregistrării dintr-o bază de date;
 un tuplu e tratat ca o unitate de dimensiune neflexibilă (ex: punctul de coordonate (x,y,z)), în
timp ce lista e un șir de lungime variabilă (i se pot adăuga, șterge, modifica elemente);
 un tuplu poate fi considerat o listă read-only.
Ambele sunt structuri ordonate (pot fi accesate/ parcurse prin indici, spre deosebire de
dicționar). Dicționarele și tuplele NU pot fi sortate direct (se convertesc în liste în acest scop).
De cele mai multe ori tabelele (sau rezultatul unor interogări SQL) este o listă de tuple:
 fiecare înregistrare e un tuplu – un set rigid de valori alăturate prin a căror alăturare se definește
o entitate (de obicei elementele unui tuplu au semnificații diferite);
 tabelul e o listă de înregistrări – o enumerare extensibilă de obiecte între care nu există diferențe
de "importanță" (de obicei elementele unei liste au semnificații și tip similare, dar nu e
obligatoriu).
Toate cele trei structuri de date sunt ETEROGENE (pot conține date de orice tip și elementele lor
pot fi la rândul lor structuri de date eterogene). Tastați următoarele atribuiri la prompterul IDLE:
Structuri complexe de date 8

>>> dictionar={'a':2,'b':[1,2,3],'c':{'x':0,5:'y'}}
>>> lista=['a',{1:'x',2:'y'},(1,2,3),5]
>>> tuplu=([1,2,3],{'a':2,'b':(10,'august',2010)},5,'a')
>>> sir='abcd'
Accesul la aceste elemente se poate face prin:
>>> dictionar['a']
2
>>> dictionar[0]
- eroare (dictionarul nu poate fi accesat prin pozitie)
>>> dictionar['b'][2]
3
>>> dictionar['c'][5]
'y'
>>> lista[0]
'a'
>>> lista[2][2]
3
>>> lista[1][1]
'x'
>>> lista[1:3]
[{1:'x',2:'y'},(1,2,3)]
- s-a returnat sublista de de la lista*1+, inclusiv, până la lista*3+, exclusiv
>>> lista[-1]
5
- s-a returnat ultimul element din listă; indicii negativi asigură accesarea elementelor dinspre
sfârşitul structurii de date!
>>> lista[-3:-1]
[{1:'x',2:'y'},(1,2,3)]
- s-a returnat bucata de listă începând cu antepenultimul element şi până la ultimul, fără acesta
>>> lista[-2:]
[(1,2,3),5]
- s-au returnat ultimele două elemente din listă
>>> sir[0]
'a'
>>> sir[-1]
'd'
- stringurile se comportă ca listele dar sunt read-only
>>> tuplu[0]
[1,2,3]
>>> tuplu[-1]
'a'
- ultimul element din tuplu
>>> tuplu[1]['b']
(10,'august',2010)
>>> tuplu[1]['b'][2]
2010
>>> tuplumic=tuplu[1:3]
>>> tuplumic
({'a': 2, 'b': (10, 'august', 2010)}, 5)
- s-a returnat subtuplul de la tuplu*1+ inclusiv, la tuplu*3+ exclusiv; chiar dacă tuplele sunt read
only, e posibilă această extragere a unui tuplu mai mic din unul mai mare, fără a-l afecta pe cel mare.
9 Lucrarea 1. Programarea în limbajul Python – structuri de date

1.4 Exerciții cu liste și stringuri


>>> lista=[]
- crearea unei liste goale, la care se pot adăuga elemente
>>>lista=['a',2,'b',1]
>>> len(lista)
4
- lungimea listei
>>> lista.append('c')
- adăugare de element la finalul listei
>>> lista
['a',2,'b',1,'c']
>>> lista2=[(2,5),(1,9)]
- s-a creat o listă nouă cu elemente de tip tuplu
>>> lista.extend(lista2)
['a', 2, 'b', 1, 'c', (2, 5), (1, 9)]
- s-au concatenat listele
>>> lista.insert(3,10)
>>> lista
['a', 2, 'b', 10, 1, 'c', (2, 5), (1, 9)]
- s-a inserat elementul 10 la poziția 3
>>> lista.insert(0,'x')
>>> lista.insert(len(lista),'x')
>>> lista
['x', 'a', 2, 'b', 10, 1, 'c', (2, 5), (1, 9), 'x']
- s-a adăugat un 'x' în față, pe poziția 0, și unul la sfârșit, pe poziția dată de lungimea curentă a
listei, echivalent cu append (lista poate conține elemente care se repetă!)
>>> lista.remove('x')
>>> lista
['a', 2, 'b', 10, 1, 'c', (2, 5), (1, 9), 'x']
- șterge primul element cu valoarea 'x' din listă
>>> lista.remove(lista[2])
>>> lista
['a', 2, 10, 1, 'c', (2, 5), (1, 9), 'x']
- s-a șters elementul cu indicele 2, adică 'b' (indicii se numerotează începând cu zero! deci
primul element este lista[0], al doilea este lista[1], etc. iar ultimul element este lista[len(lista)-1])
>>> lista.pop()
'x'
>>> lista
['a', 2, 10, 1, 'c', (2, 5), (1, 9)]
- pop șterge ȘI returnează ultimul element al listei
>>> lista.pop(2)
10
>>> lista
['a', 2, 1, 'c', (2, 5), (1, 9)]
- dacă i se dă un indice, pop șterge ȘI returnează elementul cu indicele respectiv (remove șterge
după valoare, pop șterge după poziție; remove NU returnează ceea ce șterge, pop returnează ceea ce
șterge, deci cu pop se pot transfera elemente ale listei înspre alte variabile ca în exemplul următor)
>>> v=lista.pop()
>>> v
(1, 9)
>>> lista
['a', 2, 1, 'c', (2, 5)]
- ultimul element, tuplul (1,9) a fost scos din listă și mutat în variabila v
>>> lista.index(2)
1
- index dă poziția elementului cu valoarea indicată, 2
Exerciții cu liste și stringuri 10

>>> lista.append('a')
>>> lista
['a', 2, 1, 'c', (2, 5), 2]
>>> lista.count(2)
2
- count numără aparițiile elementului cu valoarea indicată, 2 (chiar dacă 2 apare de 3 ori, a doua
apariție nu e numărată, deoarece e parte componentă a elementului de tip tuplu (2,5) și nu un element
al listei)
>>> lista.sort()
>>> lista
[1, 2, 2, 'a', 'c', (2, 5)]
- sortează crescător lista
>>> lista.append([1,2,3])
>>> lista.append([2,1])
>>> lista.append({'a':2,'b':1})
>>> lista.sort()
>>> lista
[1, 2, 2, {'a': 2, 'b': 1}, [1, 2, 3], [2, 1], 'a', 'c', (2, 5)]
- s-au adăugat trei elemente noi – două de tip listă, unul de tip dicționar (a se remarca ordinea
sortării: mai întâi numerele, apoi dicționarele, apoi listele, apoi stringurile, apoi tuplele; între elemente
de același tip, precum *1,2,3+ și *2,1+, ordinea e lexicografică, adică dată de primul element al fiecărei
liste: 1 versus 2; dacă primul element e identic se ia al doilea etc.)
>>> lista.reverse()
>>> lista
[(2, 5), 'c', 'a', [2, 1], [1, 2, 3], {'a': 2, 'b': 1}, 2, 2, 1]
- s-a inversat ordinea listei (reverse NU înseamnă sortare inversă, ci doar inversarea ordinii
CURENTE din listă, chiar dacă aceasta nu e sortată).
Ca alternativa la lista.sort() se poate folosi si functia sorted(lista):
1. lista.sort() stochează lista sortată în locul listei inițiale; sorted returnează lista sortată,
neafectând lista inițială;
2.sort() se aplică strict la liste, sorted poate sorta date din stringuri, tuple și dicționare, fără să
afecteze acele tuple/dicționare deoarece trimite rezultatul sortării în altă variabilă)
>>> del lista[1:4]
>>> lista
[(2, 5), [1, 2, 3], {'a': 2, 'b': 1}, 2, 2, 1]
- del funcționează similar cu pop, dar poate șterge bucăți de listă, în acest caz se șterge bucata
începând cu lista*1+ și până la (dar fără acesta) lista*4+
>>> del lista[:]
>>> lista
[]
- del permite și golirea listei
>>> range(2,5)
[2, 3, 4]
- funcția range generează șiruri de numere naturale, utile în cicluri for, deoarece în Python NU
EXISTĂ construcție for i=0 to n – ea se exprimă prin for i in range(n)
>>> range(10,2,-3)
[10, 7, 4]
- funcția permite și generare de progresii aritmetice, al treilea argument fiind pasul progresiei
>>> a='xyz'
>>> a[1]
'y'
>>> len(a)
3
>>> list(a)
['x', 'y', 'z']
11 Lucrarea 1. Programarea în limbajul Python – structuri de date

- stringurilePython sunt similare listelor sau se pot converti în liste; majoritatea funcțiilor care
acceptă o listă ca argument, acceptă și un string, considerând fiecare caracter un element de listă, vezi
exemplul următor:
>>> lista=[1,2,3]
>>> a='xyz'
>>> lista.extend(a)
>>> lista
[1, 2, 3, 'x', 'y', 'z']
- o diferenţă importantă între string şi liste e că stringurile sunt read-only: nu suportă modificări
sau ştergeri de caractere în mod direct:
>>> del a[2]
>>> a.extend('abc')
>>> a[1]='k'
- toate 3 dau eroare! soluţia e să se copieze stringul modificat într-unul nou:
>>> a1=a[:2]
>>> a1
'xy'
>>> a2=a+'abc'
>>> a2
'xyzabc'
>>> a3=a[:1]+'k'+a[2:]
>>> a3
'xkz'

Matricile sunt liste de liste:


>>> rand1=[3,4,2]
>>> rand2=[5,'a','b']
>>> matrice=[rand1,rand2]
>>> matrice
[[3, 4, 2], [5, 'a', 'b']]
- matricile sunt eterogene, pot conține date de orice tip, deci pot fi și liste de tuple, dacă
matricea nu va suferi modificări.
Exerciții cu tuple 12

1.5 Exerciții cu tuple


>>> tuplugol=()
- crearea unui tuplu gol
>>> tuplusingle=(2,)
- crearea unui tuplu cu un element – e obligatorie prezența virgulei finale, altfel se consideră că
paranteza are rol matematic, ca în formula (3x2)+(2x8)) (obs:având în vedere că tuplele sunt read-only –
nu pot fi extinse, elementele lor nu pot fi modificate – de obicei nu are rost să se creeze tuple cu 0 sau 1
element, dar sunt unele operații care dau ca REZULTAT astfel de tuple)
>>> tuplu=(2,4,'c',[5,'x'])
>>> len(tuplu)
4
- lungimea tuplului

Comenzi ca append, insert, pop, remove, insert, sort nu sunt disponibile – tuplele nu se pot
modifica, deci nici sorta direct.
13 Lucrarea 1. Programarea în limbajul Python – structuri de date

1.6 Exerciții cu dicționare


Deoarece dicţionarele nu au conceptul de ordine, nu există o ordine fixată între elemente,
acestea fiind afişate în diverse ordini ce diferă de la un calculator la altul, de la un moment la altul. E
posibil ca în unele din exemplele următoare, la afişarea de dicţionare ordinea elementelor din acest
document să difere de cea pe care o obţineţi pe monitor. Totuşi, ordinea elementelor rămâne stabilă
între două operaţii care afectează conţinutul dicţionarului (adăugări, ştergeri, modificări).
>>> dictionar={}
- crearea unui dictionar gol
>>>dictionar={'Ion':4, 'Andrei':8,'Ana':10}
>>> dictionar.sort()
- eroare! dictionarele NU se pot sorta direct (nu au ordine); se pot sorta datele din dicţionar,
extrase sub formă de liste, se va reveni ulterior la astfel de exemple
>>> dictionar[2]
- eroare! dictionarele NU permit acces prin indici, și nici un fel de operație bazată pe poziție:
index,pop cu poziție,insert,remove,append
>>> dictionar.keys()
['Ion', 'Andrei', 'Ana']
- cheile dicționarului, sub formă de LISTĂ (deci sortabile)!
>>> dictionar.values()
[4, 8, 10]
- valorile dicționarului, sub formă de LISTĂ (cele două liste pot fi sortate, dar dicționarul în
ansamblu NU! ;dictionarul mai poate fi sortat dacă e convertit în LISTĂ de tuple-perechi)
>>> dictionar['Maria']=10
>>> dictionar
{'Ion': 4, 'Andrei': 8, 'Ana': 10, 'Maria': 10}
- adăugarea de elemente în dicționar se face prin atribuiri directe, NU cu append/insert
>>> dictionar['Maria']=3
>>> dictionar
{'Ion': 4, 'Andrei': 8, 'Ana': 10, 'Maria': 3}
- adăugarea unui element cu o cheie care există deja în dicționar va înlocui elementul existent;
așadar, cheile dicționarului trebuie să fie UNICE
>>> del dictionar['Maria']
>>> dictionar
{'Ion': 4, 'Andrei': 8, 'Ana': 10}
- ștergerea de elemente se poate face cu del, dar aceasta NU poate fi folosită ca la liste, cu indici
de poziție
>>> dictionar={'Ion': 4, 'Andrei': 8, 'Ana': 10}
>>> dictionar.pop('Ana')
10
- ștergerea e posibilă și cu pop, dar nu după poziție, ci DUPĂ CHEIE; ca și la liste, pop returnează
valoarea ștearsă
>>> dictionar.popitem()
('Ion', 4)
>>> dictionar
{'Andrei': 8}
- popitem nu primește argumente, iar elementul șters e aleatoriu! se observă că funcția
returnează elementul șters sub formă de TUPLU, ('Ion',4)! funcția e utilă când se face distrugerea
incrementală, pas cu pas, a unui dicționar, sau conversia dicționarului în listă de tuple, fără a conta
ordinea în care se extrag elementele
>>> dictionar={'Ion': 4, 'Andrei': 8, 'Ana': 10}
>>> dictionar.setdefault('Maria',5)
5
>>> dictionar
{'Ion': 4, 'Andrei': 8, 'Ana': 10, 'Maria': 5}
Operații care funcționează similar pe liste, tuple și dicționare 14

>>> dictionar.setdefault('Maria',10)
5
>>> dictionar
{'Ion': 4, 'Andrei': 8, 'Ana': 10, 'Maria': 5}
- setdefault adaugă o pereche nouă la dicționar DACĂ nu există deja cheia, și îi returnează
valoarea; DACĂ există deja cheia, îi returnează valoarea existentă și nu adaugă nimic; operația e foarte
utilă deoarece ne scutește de face un test IF asupra existenței unui element)
>>>dictionar.get('Ana','nu exista')
10
>>> dictionar.get('Alin','nu exista')
'nu exista'
- get citeşte o valoare din dicţionar DACĂ există cheia, altfel returnează al doilea argument; din
nou, o funcţie utilă pentru că înglobează un test IF util la filtrarea dicţionarelor
>>> dictionar1={'Ion': 4, 'Andrei': 8, 'Ana': 10}
>>> dictionar2={'Ion':7,'Maria':10}
>>> dictionar1.update(dictionar2)
>>> dictionar1
{'Ion': 7, 'Andrei': 8, 'Ana': 10, 'Maria': 10}
- update reunește două dicționare, înlocuind eventualele elemente cu aceeași cheie, 'Ion' în
cazul de față (reunirea funcționează și între un dicționar și o listă de tuple, de exemplu
[('Ion',7),('Maria',10)] în loc de dicționarul al doilea)
>>> dictionar=dict(Ana=5,Maria=10)
>>> dictionar
{'Ana': 5, 'Maria': 10}
- având în vedere dificultatea tastării dicționarelor, unii preferă această sintaxă mai eficientă!
>>> dictionar.clear()
>>> dictionar
{}
(clear golește un dicționar; listele se golesc cu del lista*:+, tuplele nu se pot goli, fiind read only )

1.7 Operații care funcționează similar pe liste, tuple și dicționare


>>> tuplu=(2,3,4)
>>> lista=['a','b','c']
>>> dictionar={'Ion':5,'Maria':6,'Andrei':7}
>>> list(tuplu)
[2,3,4]
>>> list(dictionar)
['Ion','Maria','Andrei']
>>> list(dictionar.values())
[5,6,7]
>>> tuple(lista)
('a','b','c')
>>> tuple(dictionar)
('Ion','Maria','Andrei')
>>> tuple(dictionar.values())
(5,6,7)
- acestea sunt metodele de conversie din orice structură de date în listă și tuplu; dicționarele se
pot converti din LISTĂ DE TUPLE, ca în cazul următor:
>>> tuplu1=('Ion',2)
>>> tuplu2=('Maria',5)
>>> lista=[tuplu1,tuplu2]
>>> dictionar=dict(lista)
>>> dictionar
{'Ion': 2, 'Maria': 5}
- e o operație frecventă mai ales după interogarea bazelor de date, care returnează seturi de
înregistrări sub formă de liste de tuple
>>> dictionar={'Ion':6,'Ana':4,'Maria':2}
>>> lista=dictionar.items()
>>> lista
15 Lucrarea 1. Programarea în limbajul Python – structuri de date

[('Ion', 6), ('Ana', 4), ('Maria', 2)]


>>> lista.sort()
>>> lista
[('Ana', 4), ('Ion', 6), ('Maria', 2)]
- cu items se obține lista de tuple din dicționar, metodă folosită când se dorește sortarea datelor
unui dicționar; evident, nu se sortează dicționarul propriu-zis, ci datele sale exprimate în altă structură;
sortarea e făcută după CHEI; după valori e mai complicată, necesită parcurgerea listei și inversarea
tuplelor – se va reveni la această problemă mai târziu (reamintim că extragerea unei liste de tuple din
dicționar se poate face și cu un ciclu for ce conține operația popitem(), dar aceasta distruge dicționarul
original!)
>>> lista=['a','b','c','d']
>>> dictionar=dict.fromkeys(lista,5)
>>> dictionar
{'a': 5, 'c': 5, 'b': 5, 'd': 5}
- fromkeys trebuie obligatoriu calificată cu clasa dict și returnează un dicționar cu cheile preluate
dintr-o listă/tuplu/string și o valoare default, aici 5
>>> tuplu=(2,3,4)
>>> lista=['a','b','c']
>>> dictionar={'Ion':5,'Maria':6,'Andrei':7}
>>> x,y,z=tuplu
>>> x
2
>>> y
3
>>> z
4
- cele trei variabile au primit ca valori elementele tuplului; condiție: numărul de variabile trebuie
să fie egal cu lungimea tuplului
>>> x,y,z=lista
>>> x
'a'
>>> y
'b'
>>> z
'c'
- cele trei variabile au primit ca valori elementele listei, nr. de variabile trebuie să fie egal cu
lungimea listei
>>> x,y,z=dictionar
>>> x
'Ion'
>>> y
'Andrei'
>>> z
'Maria'
- cele trei variabile au primit ca valori cheile/etichetele dicționarului, nr. de variabile trebuie să
fie egal cu numărul de chei; dacă se dorește preluarea valorilor din dicționar, se folosește funcția values:
>>> x,y,z=dictionar.values()
>>> x
5
>>> y
7
>>> z
6
{'Ion': 4, 'Andrei': 8, 'Ana': 10}
>>> x='Ion'
>>> x in dictionar
True
>>> lista=[1,2,3]
>>> 2 in lista
True
>>> tuplu=(1,2,3)
>>> 1 in tuplu
Operații care funcționează similar pe liste, tuple și dicționare 16

True
- cu in, se poate verifica apartenența unei variabile la tuplu, lista sau dicționar; în cazul
dicționarului se verifică doar apartenența la cheile dicționarului; pentru valori se va verifica apartenența
la dictionar.values() (la fel se lucrează cu not in)
>>> tuplu=(4,5,6)
>>> lista=[4,5,6]
>>> dictionar={4:'Ion',5:'Ana',6:'Andrei'}
>>> sum(tuplu)
15
>>> sum(lista)
15
>>> sum(dictionar)
15
- fiind o operație frecventă, însumarea elementelor unui șir de numere se face direct cu sum; în
cazul dicționarului se însumează DOAR cheile! pentru valori, se apelează sum(dictionary.values())
>>> sum(tuplu,10)
25
- se poate preciza valoarea de pornire a sumei, la care se adaugă suma propriu-zisă, aici 10
>>> min(tuplu)
4
>>> max(dictionar)
6
- similar funcţionează şi funcţiile de căutare a minimului sau maximului
>>> min(2,4,1)
1
- dacă cele două funcţii primesc mai mult de un argument, se caută min/max dintre argumente;
dacă argumentul e unic se consideră că e o structură de date şi se caută min/max dintre elementele sale
>>> tuplu1=('Ion','Ana','Andrei')
>>> tuplu2=(4,6,5)
>>> (tuplu1,tuplu2)
[('Ion', 4), ('Ana', 6), ('Andrei', 5)]
>>> lista1=['Ion','Ana','Andrei']
>>> lista2=[4,6,5]
>>> zip(lista1,lista2)
[('Ion', 4), ('Ana', 6), ('Andrei', 5)]
>>> dictionar1=dict(Ion=4,Ana=6,Andrei=5)
>>> dictionar2=dict(x=1,y=3,z=2)
>>> zip(dictionar1,dictionar2)
[('Ion', 'y'), ('Andrei', 'x'), ('Ana', 'z')]
- funcția zip generează o listă de tuple alăturând elementele de pe aceeași poziție din două
tuple, liste sau dicționare; în cazul dicționarelor alătură DOAR cheile!; pt. a alătura valorile sau cheile din
primul cu valorile din al doilea se va jongla cu dictionar.values())

Observație: deoarece zip se bazează pe poziţia elementelor, poate fi problematică utilizarea sa împreună
cu dicţionarele, unde poziţia e arbitrară; exemplul de mai sus poate da rezultate diferite de la o execuţie
la alta; totuşi, poziţia elementelor de dicţionar rămâne STABILĂ între două modificări suferite de
dicţionar deci în aceste limite, zip dă rezultate stabile, dar diferite de la un calculator la altul.
>>> zip(tuplu1,lista1)
[('Ion', 'Ion'), ('Ana', 'Ana'), ('Andrei', 'Andrei')]
- se pot alătura elemente din structuri de tip diferit-un tuplu și o listă, un tuplu și un dicționar
etc.
>>> zip(tuplu1)
[('Ion',), ('Ana',), ('Andrei',)]
- dacă funcția primește un singur argument, se generează tuple cu element unic
>>> zip(tuplu1,tuplu2,lista2)
[('Ion', 4, 4), ('Ana', 6, 6), ('Andrei', 5, 5)]
>>>
17 Lucrarea 1. Programarea în limbajul Python – structuri de date

- funcția poate primi mai multe structuri, nu neapărat două, generând tuple de diferite lungimi;
funcția zip e foarte utilă când se dorește parcurgerea simultană a mai multor structure de date! dacă
structurile sunt de lungimi diferite, zip se OPREȘTE la finalul celei mai scurte structuri!
>>> listatuple=zip(tuplu1,lista2,dictionar2)
>>> listatuple
[('Ion', 4, 'y'), ('Ana', 6, 'x'), ('Andrei', 5, 'z')]
>>> a,b,c=zip(*listatuple)
>>> a
('Ion', 'Ana', 'Andrei')
>>> b
(4, 6, 5)
>>> c
('y', 'x', 'z')
- dacă * apare în fața argumentului unei funcții și acel argument este o listă sau un tuplu, este
"spart" în elementele componente; în cazul de față zip(*listatuple) echivalează cu zip(('Ion', 4, 'y'),
('Ana', 6, 'x'), ('Andrei', 5, 'z')); cu alte cuvinte, tuplele sunt extrase din listă și oferite funcției ca valori
separate; pentru mai bună înțelegere, urmăriți și exemplul:
>>> numere=[3,8]
>>> range(*numere)
[3,4,5,6,7]
- în acest caz lista *3,8+ a fost spartă în elementele 3,8 care au fost transferate funcției,
executându-se de fapt range(3,8); metoda este utilă atunci când argumentele unei funcții sunt prezente
ca elemente ale unei liste/tuplu și ne scutește de a tasta, pt. cazul de față,
range(numere*0+,numere*1+)); în mod similar, se poate sparge și un dicționar într-o listă de argumente,
dacă apelul are forma functie(*dictionar.keys())sau functie(*dictionar.values())
>>> dictionar={'Ion':6,'Ana':4,'Maria':2}
>>> lista=zip(dictionar.values(),dictionar.keys())
>>> lista
[(6, 'Ion'), (4, 'Ana'), (2, 'Maria')]
- chiar dacă poziţia elementelor din dicţionare e arbitrară, aceasta e stabilă atâta vreme cât
dicţionarul nu suferă modificări, deci nu există pericolul ca zip să citească valorile şi cheile în ordini
diferite!
>>> lista.sort()
>>> lista
[(2, 'Maria'), (4, 'Ana'), (6, 'Ion')]
- din acest exemplu reiese că zip se poate folosi cu succes la sortarea datelor din dicționare
DUPĂ VALOARE! reamintim că sortarea DUPĂ CHEI se poate face convertind dicționarul în listă de tuple,
cu funcția items())
>>> lista=['x','y','z']
>>> tuplu=('x','y','z')
>>> dictionar={'x':2,'y':3,'z':4}
>>> ''.join(lista)
'xyz'
>>> ''.join(tuplu)
'xyz'
>>> ''.join(dictionar)
'yxz'
- dacă elementele din structurile de date sunt stringuri, acestea se pot concatena cu join; în
cazul dicționarului se concatenează CHEILE, dacă se doresc valorile, se lucrează cu dictionar.values();
stringul din fața lui join, aici șirul vid, capătă rol de delimitator, vezi cazul următor:
>>> '00'.join(lista)
'x00y00z'
>>> lista=[3,1,2]
>>> tuplu=(3,1,2)
>>> sir='312'
>>> dictionar={3:'x',1:'a',2:'y'}
>>> sorted(lista)
[1, 2, 3]
>>> lista
Operații care funcționează similar pe liste, tuple și dicționare 18

[3, 1, 2]
>>> sorted(tuplu)
[1, 2, 3]
>>> tuplu
(3, 1, 2)
>>> sorted(sir)
['1', '2', '3']
>>> sir
'312'
>>> sorted(dictionar)
[1, 2, 3]
>>> sorted(dictionar.values())
['a', 'x', 'y']
- spre deosebire de sort() care funcționează doar cu liste, sorted se poate aplica la tuple,
stringuri, liste, dicționare, fără a afecta valorile inițiale ale acestora; sorted returnează o listă sortată ce
poate fi atribuită unei variabile pentru prelucrări ulterioare; ordinea descrescătoare poate fi aplicată cu
reverse() asupra listei rezultate, sau direct în timpul sortării dacă se apelează
sorted(tuplu,None,reverse=True); None e locțiitor pentru apelul unei eventuale funcții ce asigură
compararea mai complexă a elementelor, de exemplu atunci când se ordonează tuple după alte criterii
decât primul element; vom reveni ulterior la astfel de exemple.
19 Lucrarea 1. Programarea în limbajul Python – structuri de date

1.8 Atribuirea şi copierea structurilor de date


Următoarele exemple vor discuta o serie de confuzii care pot apare cu privire la atribuirea şi
copierea structurilor de date:
lista1=[1,2,3]
>>>lista2=lista1
- am fi tentaţi să credem că prin această atribuire s-a creat o copie independentă a listei1; în
realitate, s-a alocat un NOU NUME aceluiaşi conţinut: lista2 va reflecta orice modificare suferă lista1!
>>> lista1.append(4)
- am adus o modificare la conţinutul curent al listei
>>> lista2
[1, 2, 3, 4]
>>> lista1
[1, 2, 3, 4]
- modificarea e reflectată de ambele liste; practic nu e vorba de două liste, ci de o singură listă
cu 2 nume!!
>>> lista1 is lista2
True

Cu is, putem verifica dacă două nume au fost alocate aceluiaşi conţinut / obiect; a nu se confunda cu
egalitatea:
- pt. lista1=*1,2+ şi lista2=*1,2+, avem egalitate: lista1==lista2, dar nu avem echivalenţă de
conţinut: lista1 is not lista2!
- pt. lista1=*1,2+ şi lista2=lista1, avem atât egalitate, cât şi echivalenţă.
…diferenţa e că egalitatea compară doar valorile, în timp ce is verifică dacă două variabile şi-au preluat
conţinutul una de la alta, deci dacă au fost alocate aceleiaşi zone de memorie)

Exemplu cu dicţionar:
>>> dictionar1={'Nume':'Alin','Nota':4}
>>> dictionar2=dictionar1
- din nou, nu e vorba de o copie, ci un nou nume alocat aceluiaşi dicţionar; cu alte cuvinte,
dictionar2 va avea mereu acelaşi conţinut cu dictionar1!
>>> dictionar1['Nota']=10
- am făcut o modificare de conţinut curent
>>> dictionar2
{'Nume': 'Alin', 'Nota': 10}
>>> dictionar1
{'Nume': 'Alin', 'Nota': 10}
- modificarea e reflectată şi de dicţionar2, mai exact avem un singur dicţionar cu două nume!

Problema nu apare la variabilele cu conţinut elementar (numeric, string, boolean etc.) şi nici la tuple
(acestea sunt read only, modificarea conţinutului lor curent nu e posibilă):
>>> a=2
>>> b=a
>>> a=5
>>> b
2
>>> a
5

Deşi pare un comportament diferit, în realitate mecanismul e acelaşi! Linia 3 (a=5) NU e o modificare de
conţinut curent cum s-ar putea intui în mod eronat, ci o realocare a numelui a spre un alt conţinut.
Atribuirea şi copierea structurilor de date 20

Confuziile pot să apară datorită modului în care înţelegem operaţia de atribuire. Există tentaţia (la
programatorii începători) de a considera că, prin a=2, valoarea 2 e transferată în variabila a. În realitate,
atribuirea fucţionează exact invers: numele a e alocat valorii 2! Ca regulă general, numele e atribuit
valorii, nu valoarea numelui! Apoi prin b=a, numele b e alocat aceleiaşi valori (2), apoi cu a=5, numele a
e realocat unei alte valori (5), fără ca b să fie afectat.

În schimb prin operaţiile din exemplele anterioare, de genul:


lista[2]=3
lista.append(3)
dictionar['Nota']=10
del dictionar['Nota']
…nu mai e vorba de realocări de nume, ci de modificări ale conţinutului curent alocat numelor lista şi
dictionar (modificări care automat se reflect asupra tuturor numelor alocate aceluiaşi conţinut!)
Dacă operaţiile ar arăta astfel:
lista=['a','b']
dictionar={2:10,3:11}
…atunci ar fi într-adevăr realocări de nume şi eventualele variabile care au primit valori prin x=lista sau
x=dictionar nu vor fi afectate de aceste realocări.

Exemplu:
>>> lista1=[1,2,3]
>>> lista2=lista1
- am alocat numele lista2 aceluiaşi conţinut ca şi lista1
>>> lista1
[1, 2, 3]
>>> lista2
[1, 2, 3]
- se vede că au acelaşi conţinut
>>>lista1[0]=999
- lista1 suferă o modificare de conţinut curent
>>> lista1
[999, 2, 3]
>>> lista2
[999, 2, 3]
- ambele liste reflectă modificarea, deoarece sunt două nume alocate aceluiaşi conţinut
>>>lista1=[3,2,1]
- aici nu mai e modificare de conţinut curent, ci realocarea numelui lista1 spre un alt conţinut!
>>> lista1
[3, 2, 1]
>>> lista2
[999, 2, 3]
- lista2 nu mai e afectată; de la acest punct încolo, lista1 şi lista2 sunt independente, au
conţinuturi diferite, nu se vor mai influenţa.

Ce se întâmplă dacă o variabilă elementară e alocată unui element de listă?


>>> lista=[1,2,3]
>>> a=lista[0]
- am alocat variabila conţinutului primului element
>>> lista[0]=99
- am modificat primul element
>>> lista
[99, 2, 3]
(lista e modificată)
>>> a
21 Lucrarea 1. Programarea în limbajul Python – structuri de date

1
(variabila elementară nu a preluat modificarea!)

La fel funcţionează şi dacă atribuim elemente de dicţionar unor variabile simple (deşi, în lumina celor
explicate mai sus, am fi tentaţi să credem că a şi lista[0] sunt 2 nume diferite ale aceluiaşi conţinut).
Explicaţia stă în faptul că în atribuirile de forma x=lista[…] sau x=dictionar[…] se face implicit o copie a
elementelor accesate şi NU doar o alocare de nume noi (asta ne sugerează şi cum se poate face efectiv o
copie independentă a unei liste!).

Se impune dilema: cum construim o nouă variabilă care să fie o copie a listei/dicţionarului
original şi nu doar un nou nume alocat acestora? Există 2 soluţii:
 una universală şi mai puternică (mai poate fi aplicată la copiere de obiecte şi copiere de
structuri de date ale căror elemente sunt tot structuri de date);
 una specifică listelor şi dicţionarelor (mai simplă dar mai superficială – nu se poate folosi la
obiecte şi asigură doar copierea de elemente simple).

Soluţia specifică listelor:


>>> lista1=[1,2,3]
>>> lista2=lista1[:]
- spre deosebire de lista2=lista1, prezenţa parantezelor asigură copierea, ca şi în cazul x=lista*…+
de mai sus!
>>> lista1[0]=100
>>> lista1
[100, 2, 3]
>>> lista2
[1, 2, 3]
- se poate vedea că lista1 şi lista2 nu mai sunt nume ale aceleiaşi liste, cele două copii sunt
independente

Soluţia specifică dicţionarelor:


>>> dictionar1={2:'Ion',3:'Ana'}
>>> dictionar2=dictionar1.copy()
- funcţia copy asigură crearea unei copii independente
>>> dictionar2
{2: 'Ion', 3: 'Ana'}
>>> dictionar1[3]=999
>>> dictionar1
{2: 'Ion', 3: 999}
>>> dictionar2
{2: 'Ion', 3: 'Ana'}
- avem două dicţionare independente şi nu un dicţionar cu 2 nume

Problema cu aceste soluţii e că sunt specifice (nu au utilitate generală, nu pot fi aplicate spre
exemplu obiectelor) şi că sunt superficiale (nu asigură copierea de elemente complexe):

>>> lista1=[1,[2,3],4]
- lista conţine o altă listă!
>>> lista2= lista1[:]
- am efectuat o copiere superficială
>>> lista1[0]=100
>>> lista2
Atribuirea şi copierea structurilor de date 22

[1, [2, 3], 4]


>>> lista1
[100, [2, 3], 4]
- modificarea primului element nu afectează lista2
>>> lista1[1][0]=100
- acum modificăm un element al elementului-listă!
>>> lista1
[100, [100, 3], 4]
>>> lista2
[1, [100, 3], 4]
…modificarea unui element din cadrul elementului de tip listă afectează lista2; cu alte cuvinte
lista2 e independentă de lista1, cu excepţia elementelor care sunt la rândul lor liste!

Asta înseamnă că s-a realizat copiere superficială.


Soluţia universal aplicabilă pentru copieri de structuri de date, care include şi instrumente de
copiere profundă e asigurată de modulul copy:

>>> import copy


>>> lista2=copy.deepcopy(lista1)
>>> lista1
[100, [100, 3], 4]
>>> lista2
[100, [100, 3], 4]
>>> lista1[1][0]=200
>>> lista1
[100, [200, 3], 4]
>>> lista2
[100, [100, 3], 4]
- copierea profundă asigură independenţa completă a celor 2 copii

Modulul copy permite şi copiere superficială echivalentă cu exemplele de mai sus, dar cu
avantajul că se poate folosi şi la obiecte:

>>> lista2=copy.copy(lista1)
>>> lista1.append(5)
>>> lista1
[1, 2, 3, 4, 5]
>>> lista2
[1, 2, 3, 4]
- modificarea listei1 nu mai afectează lista2
>>> dictionar2=copy.copy(dictionar1)
>>> dictionar1['Nota']=1
>>> dictionar1
{'Nume': 'Alin', 'Nota': 1}
>>> dictionar2
{'Nume': 'Alin', 'Nota': 10}
- la fel, pentru dicţionare
Mai există o serie de operaţii relevante care necesită instrumente mai avansate, se va reveni
ulterior:
 returnarea cheii pentru o valoare aleasă din dicţionar (sau a cheilor, căci valoarea se poate
repeta;
 inversarea rolurilor de cheie şi valoare într-un dicţionar;
 sortarea unei liste de tuple după alt element decât primul (din fiecare tuplu);
 sortarea unei liste după alte criterii decât valoarea elementelor.
23 Lucrarea 1. Programarea în limbajul Python – structuri de date

1.9 Stive, cozi, mulțimi, imagini de dicționar


Stivele sunt structuri de tip LIFO (ultimul element adăugat e primul element accesat). Cozile sunt
structuri de tip FIFO (cel mai vechi element adăugat e primul element accesat). Ambele pot fi modelate
prin liste.

>>> stiva=[2,4,3]
>>> stiva.append(10)
>>> stiva
[2, 4, 3, 10]
>>> a=stiva.pop()
>>> b=stiva.pop()
>>> c=stiva.pop()
>>> [a,b,c]
[10, 3, 4]
>>> stiva
[2]
- variabilele a,b,c conțin elementele în ordinea scoaterii lor din listă (obs: append+pop asigură
mecanismul de tip stivă de date

Pentru cozi, s-ar putea folosi insert și pop la poziția zero, dar performanța e slabă – fiecare
inserare declanșează schimbarea poziției/indicelui pt. fiecare element; de aceea, pt. cozi se preferă
folosirea unei bibliotecii deque, ca în exemplul următor:
>>> from collections import deque
- se importă funcțiile deque din fișierul collections.py
>>> lista=[2,4,3]
>>> coada=deque(lista)
- se convertește lista în coadă; din acest moment, pe coadă se poate aplica funcția popleft(),
similară cu pop(0), dar de performanță superioară
>>> coada.append(10)
>>> a=coada.popleft()
>>> b=coada.popleft()
>>> c=coada.popleft()
>>> [a,b,c]
[2, 4, 3]
- a,b,c stochează elementele scoase din coadă cu popleft

Mulțimile nu permit repetarea de elemente. Se pot obține din liste, tuple, dicționare, chiar și
stringuri. Sunt folosite pentru operații tipice (reuniune, intersecție) sau pt. a verifica apartenența unui
element la o mulțime.

>>> lista1=[2,4,5]
>>> lista2=[4,5,4,3]
>>> a=set(lista1)
>>> b=set(lista2)
>>> a
set([2, 4, 5])
>>> b
set([3, 4, 5])
- prin conversia în mulțime, repetiția elementelor a fost anulată
>>> a[0]
eroare! prin conversia în mulțime s-a pierdut și ordinea dintre elemente, deci și poziția
>>> a-b
set([2])
>>> b-a
Stive, cozi, mulțimi, imagini de dicționar 24

set([3])
- diferența între mulțimi
>>> a|b
set([2, 3, 4, 5])
- reuniunea
>>> a&b
set([4, 5])
- intersecția
>>> a^b
set([2, 3])
- diferența simetrică, (a-b)|(b-a) (obs: a|=b, a&=b, a^=b, a-=b realizează aceleași operații, dar
memorează rezultatul în mulțimea a, modificând-o; în absența egalului, rezultatul este doar
afișat/returnat)
>>> a.add(9)
>>> a
set([9, 2, 4, 5])
- adaugă elemente, similar cu append dar fără a ține cont de poziție
>>> a.discard(9)
>>> a
set([2, 4, 5])
- șterge elemente, similar cu remove de la liste
>>> a.pop()
2
>>> a
set([4, 5])
- șterge ALEATORIU un element și îl returnează
>>> a.isdisjoint(b)
False
- testează dacă mulțimile sunt disjuncte, adică nu au elemente comune
>>> 3 in b
True
- verificarea apartenenței la mulțime, se poate folosi și not in
>>> a
set([4, 5])
>>> b
set([3, 4, 5])
>>> a<b
True
>>> a>b
False
- testează dacă o mulțime e inclusă în alta; se poate folosi și <=,>= dacă mulțimile pot fi și
echivalente; a nu se confunda comparația între mulțimi, bazată pe incluziune, cu comparația între
mărimile acestor mulțimi, care se aplică între len(a) și len(b), sau comparația între tuple, care e
lexicografică
>>> c=set('Andreea')
>>> c
set(['A', 'a', 'e', 'd', 'n', 'r'])
>>> d=set({'Ion':4,'Ana':3})
>>> d
set(['Ion', 'Ana'])
- mulțimile se pot obține și din stringuri, tuple sau dicționare
>>> d.clear()
>>> d
set([])
- clear() golește o mulțime
25 Lucrarea 1. Programarea în limbajul Python – structuri de date

O imagine de dicționar este un tip special de mulțime ce reflectă conținutul dicționarului (fie
cheile, fie valorile, fie tuple cu ambele) și are calitatea că fiecare modificare a dicționarului se transmite
automat la aceste imagini de dicționar.
>>> dictionar={3:'x',1:'a',2:'y'}
>>> imagine1=dict.viewitems(dictionar)
>>> imagine1
dict_items([(1, 'a'), (2, 'y'), (3, 'x')])
>>> imagine2=dict.viewkeys(dictionar)
>>> imagine2
dict_keys([1, 2, 3])
>>> imagine3=dict.viewvalues(dictionar)
>>> imagine3
dict_values(['a', 'y', 'x'])
>>> dictionar2={4:'a',3:'b'}
>>> dictionar.update(dictionar2)
>>> dictionar
{1: 'a', 2: 'y', 3: 'b', 4: 'a'}
>>> imagine1
dict_items([(1, 'a'), (2, 'y'), (3, 'b'), (4, 'a')])
>>> imagine2
dict_keys([1, 2, 3, 4])
>>> imagine3
dict_values(['a', 'y', 'b', 'a'])
- dictionarul s-a modificat prin update, și imaginile odată cu acesta

Imaginile de dicționare se comportă ca mulțimile, adică:


- suportă reuniune, intersecție, etc.;
- nu au ordine și nu suportă operații ce lucrează cu indici/poziție;
…dar spre deosebire de mulțimi:
- acceptă elemente repetate
- nu sunt modificabile, ci reflectă întotdeauna conținutul dicționarului.

Tabelele și arborii pot fi modelați cu diverse combinații de dicționare, liste și tuple; de regulă
interogarea bazelor de date returnează liste de tuple, cu fiecare tuplu corespunzând unei înregistrări:
inregistrari=[('ID1','Ion',4),('ID2','Ana',8),('ID3','Andrei',7)]
Uneori acestea se convertesc în dicționare de înregistrări calificate cu IDuri (valorile cheii
primare):
inregistrari={'ID1':{'Nume':'Ion','Nota':4}, 'ID2':{'Nume':'Ana','Nota':8},….}
sau lista de dictionare:
inregistrari=[{'ID':'ID1','Nume':'Ion','Nota':4},{'ID':'ID2','Nume':'Ana','Nota':8}…]

Cel mai frecvent datele extrase din surse tabelare sunt stocate în listă de tuple, deoarece astfel
înregistrările pot fi sortate înainte de afișarea lor.

Pentru arbori există modulul xml.etree.ElementTree, dar acesta e strâns legat de lucrul cu fișiere
XML și nu face subiectul direct al acestui material.
Listele generate 26

1.10 Listele generate

Listele generate sunt o metodă rapidă de a crea liste prin iterații. Suportă și filtrare cu condiții If.

>>> lista1=[2,4,3]
>>> lista2=[x*x for x in lista1]
>>> lista2
[4, 16, 9]
>>> lista3=[x*x for x in lista1 if x%2==0]
>>> lista3
[4, 16]
>>> lista4=['a'+str(x) for x in lista1]
>>> lista4
['a2', 'a4', 'a3']
>>> lista5=[(str(x),x) for x in lista1 if x>2]
>>> lista5
[('4', 4), ('3', 3)]
- generarea unei liste de tuple
>>> lista6=[x*y for x in lista1 for y in range(3)]
>>> lista6
[0, 2, 4, 0, 4, 8, 0, 3, 6]
- generare de listă cu 2 cicluri FOR: se înmulțește fiecare număr din lista1 cu fiecare număr din
range(3)=[0,1,2]
>>> tuplu1=('a','b','c')
>>> dictionar1={'Ion':4,'Ana':8,'Maria':3}
>>> lista7=[(x,y) for x in tuplu1 for y in dictionar1.values()]
>>> lista7
[('a', 4), ('a', 8), ('a', 3), ('b', 4), ('b', 8), ('b', 3), ('c', 4), ('c', 8), ('c', 3)]
- alt exemplu cu 2 cicluri FOR: s-au creat tuple combinând fiecare element din tuplu1 cu fiecare
valoare din dicționar
>>> lista8=[(x,y) for (x,y) in zip(tuplu1,dictionar1.values())]
>>> lista8
[('a', 4), ('b', 8), ('c', 3)]
- când se dorește ca cele două cicluri FOR să nu parcurgă toate combinațiile, ci să parcurgă
simultan cele două colecții, în paralel, se folosește zip; aici se parcurg în paralel tuplul și valorile
dicționarului; rezultatul poate varia datorită instabilităţii ordinii valorilor din dicţionar
>>> a='xyz'
>>> lista9=[x for x in a]
>>> lista9
['x', 'y', 'z']
>>> lista10=[(x,a.find(x)) for x in a]
>>> lista10
[('x', 0), ('y', 1), ('z', 2)]
- după cum am indicat deja, stringurile pot fi tratate ca liste de caractere; în acest caz se
returnează tuple cu fiecare caracter și poziția sa, determinată cu funcția find
>>> a=[1,2,3]
>>> b=[10,20,30]
>>> lista11=[a[x]*b[x] for x in range(len(a))]
>>> lista11
[10, 40, 90]
- o altă metodă de parcurgere simultană, folosind indice de poziție comun; se poate aplica la
liste și tuple, la dicționare devine obligatorie folosirea funcției zip deoarece nu se poate folosi indicele de
poziție (obs: în Python NU se poate folosi for x=0 to n, ci doar for x in range(n+1))
>>> lista12=[lista1[x] for x in range(len(lista1)-1,-1,-1)]
>>> lista12
[3, 4, 2]
27 Lucrarea 1. Programarea în limbajul Python – structuri de date

- inversarea listei1, similar cu reverse() dar rezultatul e salvat în lista nou generată; reamintim că
range(a,b,c) este lista numerelor începând cu a, până la b (dar fără acesta), cu pasul c; în acest caz e
range(2,-1,-1)=*2,1,0+, adică toate pozițiile din listă de la sfârșit spre început
>>> tuplu=('abc','xyz','123')
>>> lista13=[sir[0] for sir in tuplu]
>>> lista13
['a', 'x', '1']
- lista primelor caractere din fiecare string
>>> lista14=[x for sir in tuplu for x in sir]
>>> lista14
['a', 'b', 'c', 'x', 'y', 'z', '1', '2', '3']
- lista tuturor caracterelor din toate stringurile (obs: din acest exemplu reiese că variabila
generatoare x NU trebuie obligatoriu să apară în primul FOR; în acest exemplu primul FOR folosește o
variabila temporară – sir, prin care extrage elementele tuplului și abia în al doilea FOR se parcurge
fiecare sir cu ajutorul variabilei generatoare x)
>>> lista15=[x for sir in tuplu if sir!='123' for x in sir if x not in 'ab']
>>> lista15
['c', 'x', 'y', 'z']
- operația e similară cu cea de mai sus, dar s-au impus două restricții IF: se evită elementul '123'
și se selectează doar caracterele diferite de 'a' și 'b'; se observă că se pot combina mai multe cicluri FOR,
fiecare cu propria restricție IF
>>> matrice=[[1,2,3],[4,5,6],[7,8,9]]
>>> diagonala=[matrice[x][x] for x in range(len(matrice))]
>>> diagonala
[1, 5, 9]
- extragerea diagonalei dintr-o matrice
>>> coloana3=[matrice[x][len(matrice)-1] for x in range(len(matrice))]
>>> coloana3
[3, 6, 9]
- extragerea ultimei coloane din matrice; reamintim că scăderea lui 1 din lungimea matricei e
necesară deoarece indicii de poziție încep de la zero, deci dacă len=3 coloane, indicele ultimei coloane e
len-1=2

Regula generală pt. generare de liste:


- pe prima poziție e șablonul de generare, folosind una sau mai multe variabile generatoare;
- pe a doua poziție e un ciclu FOR; dacă acesta trebuie să parcurgă în paralel mai multe
structuri de date, structurile se împachetează într-una singură cu zip;
- pe a treia poziție poate urma orice combinație de cicluri FOR și restricții IF aplicate
variabilelor de parcurgere ale ciclurilor FOR precedente;

Similar cu exemplele anterioare se poate executa generare de matrici, dar trebuie ținut cont că
acestea sunt LISTE DE LISTE:
>>> matrice2=[[x*x for x in rand] for rand in matrice]
>>> matrice2
[[1, 4, 9], [16, 25, 36], [49, 64, 81]]
- o matrice cu pătratele elementelor din matricea inițială; a se compara cu următorul rezultat:
>>> matrice2=[x*x for x in matrice]
- eroare! for x in matrice va asocia lui x elementele LISTEI matrice, care sunt la rândul lor LISTE
(rânduri);
o altă tentativă de a obține pătratele din matrice:
>>> matrice2=[x*x for rand in matrice for x in rand]
Listele generate 28

>>> matrice2
[1, 4, 9, 16, 25, 36, 49, 64, 81]
- de data asta parcurgerea e corectă, cu 2 FORURI imbricate dar are loc generarea unei LISTE, nu
a unei matrici => prima variantă e singura corectă, deoarece se generează o LISTĂ de LISTE
>>> matricetranspusa=[[rand[x] for rand in matrice] for x in range(len(matrice))]
>>> matricetranspusa
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
- pentru fiecare x din *0,1,2+ se generează o listă cu valorile de pe coloana x

Comparați cu:
m=[x*x for rand in matrice for x in rand]
- se returnează listă iar execuția ciclurilor FOR are loc de la stânga la dreapta:
 se selectează primul rând (primul FOR);
 se parcurge rândul, returnând valorile (al doilea FOR);
 se selectează al doilea rând și se reia procesul.
m=[[x*x for x in rand] for rand in matrice]
- se returnează matrice iar execuția ciclurilor FOR are loc de la dreapta la stânga:
 se selectează primul rând (al doilea FOR);
 se parcurge rândul, returnând o LISTĂ (primul FOR);
 se selectează al doilea rând și se reia procesul.

Listele generate au două aplicaţii importante la dicţionare:

Returnarea cheii pentru o valoare dată dintr-un dicţionar:


>>> dictionar={'Ion':1,'Andrei':3,'Maria':2}
>>> [cheie for cheie in dictionar if dictionar[cheie]==2]
['Maria']
- s-a returnat cheia valorii 2; cheia e returnată ca listă generată deoarece valorile se pot repeta
în dicţionar, caz în care se returnează mai multe chei!

Inversarea rolurilor de cheie şi valoare în dicţionar:


>>> dict([(dictionar[x],x) for x in dictionar])
{1: 'Ion', 2: 'Maria', 3: 'Andrei'}
- funcţionează dacă valorile dicţionarului respectă condiţia de unicitate a cheilor

Listele generate au o aplicaţie importantă în sortarea listelor de secvenţe:

S-a arătat deja că sortarea normală este numerică (la numere) sau lexicografică (după primul
caracter care diferă, la liste, stringuri, tuple, etc.). Sunt situaţii în care dorim ca sortarea să se facă după
alt criteriu. Mecanismul poartă numele DSU (decorate-sort-undecorate).

De exemplu, putem ordona o listă de stringuri după lungimea stringului:


>>> lista=['a','abc','ab']
>>> listadecorata=[(len(x),x) for x in lista]
>>> listadecorata
[(1, 'a'), (3, 'abc'), (2, 'ab')]
- etapa de decorare – s-a construit o listă de tuple ce conţine pe prima poziţie criteriul de
sortare, obţinut cu funcţia len, iar pe al doilea elementele de sortat
29 Lucrarea 1. Programarea în limbajul Python – structuri de date

>>> listads=sorted(listadecorata)
>>> listads
[(1, 'a'), (2, 'ab'), (3, 'abc')]
- etapa de sortare a listei decorate
>>> listasortata=[y for (x,y) in listads]
>>> listasortata
['a', 'ab', 'abc']
- etapa undecorate – recuperarea listei originale din lista sortată decorată

Exemplificăm aceeaşi tehnică în sortarea unor tuple după al doilea element:

>>> listatuple=[(1,2,3),(8,1,9),(3,0,4)]
>>> listadecorata=[(x[1],x) for x in listatuple]
>>> listadecorata
[(2, (1, 2, 3)), (1, (8, 1, 9)), (0, (3, 0, 4))]
>>> listads=sorted(listadecorata)
>>> listads
[(0, (3, 0, 4)), (1, (8, 1, 9)), (2, (1, 2, 3))]
>>> listasortata=[y for (x,y) in listads]
>>> listasortata
[(3, 0, 4), (8, 1, 9), (1, 2, 3)]

Ulterior (în capitolul de funcţii) vom arăta că mecanismul DSU poate deveni mai facil dacă
partea de decorare o alocăm unei funcţii.
Bibliografie 30

1.11 Bibliografie
D. M. Beazley, Python Essential Reference, Pearson Education, 2009
J. M. Zelle, Python programming: an introduction to computer science, Ed. Wilsonville: Franklin, Beedle,
2004
M.Lutz, Programming Python,O'Reilly, 2011.

S-ar putea să vă placă și