Documente Academic
Documente Profesional
Documente Cultură
Python
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
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
for x in colectie:
comenzile blocului
sau
if a==0:
primul bloc
else:
bloc alternativ
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!
Î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
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
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):
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
easy_install –h
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
>>>
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
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
a='Ion'
b='Maria'
c={a:4,b:5}
devine echivalent cu
c={'Ion':4,'Maria':5}
– 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}
Î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}
>>> 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
>>> 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'
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
>>> 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 )
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
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.
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.
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).
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
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
>>> 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
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
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
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.
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).
>>> 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ă
>>> 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.