5. Structuri de date¶
Capitolul de față se apleacă mai în detaliu asupra unor chestiuni cu care v-ați întâlnit deja, iar acestora le adaugă, firește, și lucruri noi.
5.1. Mai multe despre liste¶
Tipul de date listă dispune de mai multe metode. Iată toate aceste metode ale obiectelor listă:
- list.append(x)
Adaugă un element la finalul listei. La fel ca
a[len(a):] = [x]
.
- list.extend(iterable)
Extinde lista prin adăugarea tuturor elementelor unui (obiect) iterabil. La fel ca
a[len(a):] = iterabil
.
- list.insert(i, x)
Inserează un element la poziția dată. Primul argument este indicele elementului înaintea căruia se va face inserția, de aceea
a.insert(0, x)
va realiza o inserție la începutul listei în timp cea.insert(len(a), x)
este echivalentă cua.append(x)
.
- list.remove(x)
Elimină primul element din listă a cărui valoare este egală cu x. Va ridica o excepție
ValueError
dacă nu există niciun astfel de element.
- list.pop([i])
Elimină din listă elementul de la poziția dată și îl returnează. Dacă nu se specifică indicele elementului, atunci
a.pop()
va elimina și va returna ultimul element al listei. Ridică o excepțieIndexError
atunci când fie lista este goală fie când indicele se găsește în afara plajei de valori a indicilor elementelor listei.
- list.clear()
Elimină toate elementele din listă. La fel ca
del a[:]
.
- list.index(x[, start[, end]])
Întoarce indicele, numerotat de la zero, al primului element al listei a cărui valoare este egală cu x. Ridică o excepție
ValueError
dacă nu există niciun asemenea element.Argumentele opționale start și end sunt interpretate la fel ca în cazul unei tranșări și sunt folosite la limitarea căutărilor într-o anumită sub-secvență a listei. Indicele returnat se calculează relativ la începutul întregii secvențe și nu începând de la argumentul start.
- list.count(x)
Returnează numărul de apariții ale lui x în listă.
- list.sort(*, key=None, reverse=False)
Sortează elementele listei păstrându-le în aceeași listă (argumentele pot fi folosite la construcția unei sortări personalizate, vezi
sorted()
pentru o justificare a lor).
- list.reverse()
Scrie în ordine inversă elementele listei păstrându-le în aceeași listă.
- list.copy()
Întoarce o copie superficială a listei. La fel ca
a[:]
.
Un exemplu care întrebuințează majoritatea metodelor listei:
>>> fructe = ['portocală', 'măr', 'pară', 'banană', 'kiwi', 'măr', 'banană']
>>> fructe.count('măr')
2
>>> fructe.count('mandarină')
0
>>> fructe.index('banană')
3
>>> fructe.index('banană', 4) # Găsește prima banană situată la dreapta poziției 4
6
>>> fructe.reverse()
>>> fructe
['banană', 'măr', 'kiwi', 'banană', 'pară', 'măr', 'portocală']
>>> fructe.append('struguri')
>>> fructe
['banană', 'măr', 'kiwi', 'banană', 'pară', 'măr', 'portocală', 'struguri']
>>> fructe.sort()
>>> fructe
['banană', 'banană', 'kiwi', 'măr', 'măr', 'pară', 'portocală', 'struguri']
>>> fructe.pop()
'struguri'
Poate că ați remarcat deja că metode precum insert
, remove
sau sort
, adică metode care fac modificări în listă păstrând lista, nu afișează nicio valoare returnată – în fapt, ele returnează valoarea prestabilită None
. [1] Acesta este un principiu de design valabil pentru toate structurile de date mutabile din Python.
Altă chestiune de remarcat este aceea că nu toate datele pot fi sortate și nici comparate. De exemplu, [None, 'salut', 10]
nu se va sorta deoarece numerele întregi nu sunt comparabile cu șirurile de caractere iar None
nu poate fi comparat cu niciun alt tip de date. De asemeni, există tipuri de date fără o relație de ordine totală. Astfel, 3+4j < 5+7j
nu este o comparație validă.
5.1.1. Utilizarea listelor ca stive¶
Metodele tipului listă ne permit să utilizăm listele cu ușurință pe post de stive, adică de instanțe ale acelui tip de date la care ultimul element adăugat va fi extras primul („ultimul intrat, primul ieșit”). Pentru a pune un element în vârful stivei, folosiți append()
. Pentru a scoate un element din vârful stivei, folosiți pop()
fără să precizați niciun index. De exemplu:
>>> stiva = [3, 4, 5]
>>> stiva.append(6)
>>> stiva.append(7)
>>> stiva
[3, 4, 5, 6, 7]
>>> stiva.pop()
7
>>> stiva
[3, 4, 5, 6]
>>> stiva.pop()
6
>>> stiva.pop()
5
>>> stiva
[3, 4]
5.1.2. Făcând cozi din liste¶
Se poate să întrebuințăm o listă aidoma unei cozi, adică a unei instanțe a acelui tip de date la care primul element adăugat va fi extras primul („primul intrat, primul ieșit”); însă listele nu au fost proiectate ca să fie eficiente în acest scop. În timp ce adăugările la final și extragerile de la finalul unei liste sunt rapide, realizarea de inserări la început și de eliminări de la începutul unei liste sunt operații lente (aceasta deoarece toate celelalte elemente trebuie deplasate în lateral cu câte o poziție).
Pentru a implementa o coadă, folosiți collections.deque
care a fost proiectată special ca să realizeze adăugiri și eliminări rapide la ambele capete. De exemplu:
>>> from collections import deque
>>> coadă = deque(["Eric", "John", "Michael"])
>>> coadă.append("Terry") # Terry intră în scenă
>>> coadă.append("Graham") # Graham intră în scenă
>>> coadă.popleft() # Primul sosit iese din scenă
'Eric'
>>> coadă.popleft() # Al doilea sosit iese din scenă
'John'
>>> coadă # Coada rămasă, în ordinea intrărilor în scenă
deque(['Michael', 'Terry', 'Graham'])
5.1.3. Comprehensiunea listelor¶
Comprehensiunea listelor (înțelegerea dar și explicarea lor) reprezintă o metodă succintă de a crea liste. Aplicațiile esențiale ale acesteia sunt construcțiile de liste noi ale căror elemente să fie rezultatul realizării anumitor operații cu fiecare din membrii unei secvențe ori ai unui iterabil, respectiv crearea unei sub-secvențe din acele elemente ale unei secvențe date care îndeplinesc o anumită condiție.
De exemplu, să presupunem că dorim să creăm o listă de pătrate perfecte, precum urmează:
>>> pătrate_perfecte = []
>>> for x in range(10):
... pătrate_perfecte.append(x**2)
...
>>> pătrate_perfecte
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Să remarcăm că procedeul anterior creează (ori suprascrie) o variabilă numită x
care va exista și după încheierea ciclului for
. Putem calcula lista pătratelor perfecte și fără asemenea efecte secundare dacă utilizăm fie expresia:
pătrate_perfecte = list(map(lambda x: x**2, range(10)))
fie pe cea echivalentă acesteia:
pătrate_perfecte = [x**2 for x in range(10)]
care este mai concisă și mai ușor de citit.
Comprehensiunea unei liste constă din paranteze drepte care încadrează o expresie urmată de o clauză for
, apoi de zero sau mai multe clauze for
ori clauze if
. Rezultatul comprehensiunii va fi o listă nouă, produsă ca urmare a evaluării expresiei în contextul clauzelor for
și if
care îi urmează în cod. De exemplu, următoarea comprehensiune de liste (în englezește, ca jargon, listcomp) combină elementele a două liste atunci când aceste elemente nu sunt egale:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
și este echivalentă cu:
>>> combinații = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combinații.append((x, y))
...
>>> combinații
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
Remarcați că ordinea instrucțiunilor for
și if
se păstrează în ambele fragmente de cod.
Dacă expresia este un tuplu (precum (x, y)
-ul din exemplul precedent), atunci ea trebuie încadrată de paranteze.
>>> vectorul = [-4, -2, 0, 2, 4]
>>> # crearea unei liste noi cu valoarea elementelor dublată
>>> [x*2 for x in vectorul]
[-8, -4, 0, 4, 8]
>>> # filtrarea listei pentru a exclude numerele negative
>>> [x for x in vectorul if x >= 0]
[0, 2, 4]
>>> # aplicarea unei funcții fiecărui element din listă
>>> [abs(x) for x in vectorul]
[4, 2, 0, 2, 4]
>>> # apelul unei metode a fiecărui element din listă
>>> fructe_proaspete = [' banană', ' hibrid de mure și zmeură ', 'fructul pasiunii ']
>>> [armă.strip() for armă in fructe_proaspete]
['banană', 'hibrid de mure și zmeură', 'fructul pasiunii']
>>> # crearea unei liste de 2-tupluri de felul (numărul, pătratul său)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # tuplul trebuie încadrat de paranteze, în caz contrar producându-se o eroare
>>> [x, x**2 for x in range(6)]
File "<stdin>", line 1
[x, x**2 for x in range(6)]
^^^^^^^
SyntaxError: did you forget parentheses around the comprehension target?
>>> # serializarea unei liste folosind o listcomp cu două 'for'-uri
>>> vectorul = [[1,2,3], [4,5,6], [7,8,9]]
>>> [numărul for elementul in vectorul for numărul in elementul]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Comprehensiunile de liste pot conține atât expresii complexe cât și funcții imbricate:
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
5.1.4. Comprehensiuni imbricate de liste¶
Expresia inițială dintr-o comprehensiune de liste poate fi orice fel de expresie, deci poate include și o (altă) comprehensiune de liste.
Să luăm în considerare următorul exemplu de matrice 3x4 implementată ca listă formată din 3 liste de lungime 4:
>>> matrice = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
Comprehensiunea de liste care urmează va transpune liniile și coloanele matricei:
>>> [[linia[i] for linia in matrice] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Așa cum am văzut în secțiunea anterioară, comprehensiunea listei interioare este evaluată în contextul for
-ul care îi urmează listei, așa că exemplul de cod este echivalent cu:
>>> transpusa = []
>>> for i in range(4):
... transpusa.append([linia[i] for linia in matrice])
...
>>> transpusa
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
cod care, la rândul său, este identic cu:
>>> transpusa = []
>>> for i in range(4):
... # următoarele 3 linii de cod implementează listcomp-a imbricată
... linia_transpusei = []
... for linia in matrice:
... linia_transpusei.append(linia[i])
... transpusa.append(linia_transpusei)
...
>>> transpusa
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
În lumea reală, preferați funcțiile predefinite oricăror instrucțiuni iterative complexe. Funcția zip()
s-ar descurca de minune în cazul de întrebuințare pe care îl studiem:
>>> list(zip(*matrice))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
A se vedea Despachetarea listelor de argumente pentru detalii despre asteriscul de pe această linie de cod.
5.2. Instrucțiunea del
¶
Există o modalitate de a elimina un element dintr-o listă atunci când îi știm indicele (poziția) dar nu și valoarea: instrucțiunea del
. Aceasta diferă de metoda pop()
care returnează valoarea elementului eliminat. Instrucțiunea del
poate fi utilizată și la eliminarea de tranșe din listă ori la golirea întregii liste (ceea ce am făcut anterior atribuindu-i tranșei o listă goală). De exemplu:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
del
poate fi folosită și la ștergerea completă a unei variabile:
>>> del a
Referențierea numelui a
de acum înainte este eronată (cel puțin până când acestui nume îi vom atribui o valoare). Îi vom găsi ulterior și alte întrebuințări lui del
.
5.3. Tupluri și secvențe¶
Am văzut că șirurile de caractere și listele au multe proprietăți în comun, cum ar fi operațiile de indexare și de tranșare. Tipurile acestor obiecte sunt exemple de tipuri de date secvență (vezi Sequence Types — list, tuple, range). Deoarece Python-ul este un limbaj aflat în plină evoluție, se poate ca și alte tipuri secvență de date să îi fie adăugate. Mai există un tip secvență de date standard: tuplul.
Un tuplu constă dintr-un număr de valori separate prin virgulă, cum ar fi:
>>> t = 12345, 54321, 'salut!'
>>> t[0]
12345
>>> t
(12345, 54321, 'salut!')
>>> # Tuplurile pot fi imbricate:
>>> u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'salut!'), (1, 2, 3, 4, 5))
>>> # Tuplurile sunt imutabile:
>>> t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # însă pot conține obiecte mutabile:
>>> v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
După cum puteți observa, atunci când sunt afișate, tuplurile apar întotdeauna încadrate de paranteze, drept pentru care tuplurile imbricate vor putea fi interpretate corect; pe de altă parte, ele pot fi introduse cu sau fără paranteze, chiar dacă adesea parantezele sunt necesare (atunci când tuplul face parte dintr-o expresie mai cuprinzătoare). Deși nu îi putem atribui valori niciunui element individual al unui tuplu, putem crea tupluri care conțin obiecte mutabile, precum listele.
Chiar dacă tuplurile par să semene cu listele, ele se folosesc adesea în situații diferite și pentru scopuri diferite de cele ale utilizării listelor. Tuplurile sunt immutable și conțin, de obicei, o secvență eterogenă de elemente care pot fi accesate fie via despachetare (după cum vom vedea ulterior în secțiunea de față) fie prin indexare (ba chiar și prin intermediul atributelor în cazul namedtuples
). În schimb, listele sunt mutable iar elementele unei liste sunt omogene, de obicei, și se accesează prin iterări de-a lungul listei.
O problemă aparte o constituie construcția de tupluri cu 0 sau 1 elemente: sintaxa impune câteva adaosuri ciudate pentru a le acomoda. Astfel, tuplurile goale se construiesc cu o pereche goală de paranteze rotunde; un tuplu cu un singur element este construit dintr-o valoare urmată de o virgulă (nu este suficient să încadrăm valoarea între paranteze). Neplăcut, dar eficient. Ca, de exemplu:
>>> gol = ()
>>> singular = 'salut', # <-- remarcați virgula de final
>>> len(gol)
0
>>> len(singular)
1
>>> singular
('salut',)
Instrucțiunea t = 12345, 54321, 'salut!'
este un exemplu de împachetare în tuplu: valorile 12345
, 54321
și 'salut!'
sunt puse laolaltă într-un tuplu. Este posibilă și operația inversă:
>>> x, y, z = t
Aceasta se numește, destul de potrivit, despachetarea secvenței și funcționează pentru orice secvență admisă ca valoare de pe partea dreaptă (ori r-valoare). Despachetarea secvențelor cere să fie tot atâtea variabile pe partea stângă a semnului egal câte elemente are secvența. Să observăm că atribuirea multiplă este, în realitate, doar o combinație de împachetări de tupluri și de despachetări de secvențe.
5.4. Seturi¶
Python-ul posedă și un tip de date pentru tratamentul mulțimilor (al seturilor). Un set este o colecție neordonată de elemente, fără duplicate. Întrebuințările de bază ale acestui tip de date includ verificarea apartenenței și eliminarea duplicatelor de elemente. Obiectele mulțime suportă, de asemeni, și operații matematice precum reuniunea, intersecția, diferența ori diferența simetrică.
Atât acoladele cât și funcția set()
pot fi folosite la crearea de mulțimi. Remarcă: pentru a crea o mulțime vidă trebuie să utilizați set()
, nu {}
; cea din urmă formulă va construi un dicționar de date gol, iar despre o asemenea structură de date vom discuta în secțiunea următoare.
Iată o scurtă demonstrație:
>>> coș = {'măr', 'portocală', 'măr', 'pară', 'portocală', 'banană'}
>>> print(coș) # arată că duplicatele au fost eliminate
{'portocală', 'banană', 'pară', 'măr'}
>>> 'portocală' in coș # testarea rapidă a apartenenței
True
>>> 'pălămidă' in coș
False
>>> # Demonstrarea operațiilor cu mulțimi într-o problemă
>>> # privind literele unicat din două cuvinte
>>>
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # litere unicat în a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # literele care sunt în a dar nu sunt în b
{'r', 'd', 'b'}
>>> a | b # literele care sunt sau în a sau în b sau în amândouă
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # literele care sunt și în a și în b
{'a', 'c'}
>>> a ^ b # literele care sunt sau în a sau în b dar nu în amândouă
{'r', 'd', 'b', 'm', 'z', 'l'}
Asemănător comprehensiunii listelor, sunt permise și comprehensiunile de mulțimi:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
5.5. Dicționare de date¶
Alt tip de date util, predefinit în Python, este dicționarul (de date, vezi Mapping Types — dict). Dicționare de date se găsesc și în alte limbaje de programare, numite uneori „memorii asociative” ori „tablouri asociative” (de la englezescul array). Spre deosebire de secvențe, ale căror elemente sunt indexate cu indici dintr-un domeniu de valori numerice, dicționarele de date au elementele indexate cu chei, acestea putând fi obiecte ale oricărui tip de date imutabile.
Cel mai bine este să ne gândim la dicționarul de date ca la o mulțime de perechi cheie: valoare pentru care se cere să avem chei unice (într-un singur dicționar). O pereche de acolade creează un dicționar gol: {}
. Plasând o listă de perechi cheie:valoare cu elementele separate prin virgule între aceste acolade, vom adăuga perechile cheie:valoare din listă la dicționarul de date; acesta este, de fapt, modul în care dicționarele sunt afișate.
Operațiile principale pe care le realizăm asupra unui dicționar sunt inserarea unei valori cu ajutorul unei anumite chei și extragerea unei valori căreia îi știm cheia. Este, de asemeni, posibil să elimnăm o pereche cheie:valoare cu ajutorul lui del
. Dacă inserăm o valoare folosind o cheie care a fost deja asociată unei anumite valori, atunci vechea valoare se va pierde. Încercarea de a extrage o valoare folosind o cheie inexistentă va produce o eroare.
Executând instrucțiunea list(d)
asupra unui dicționar de date d
, se va returna lista tuturor cheilor utilizate de dicționar, în ordinea în care ele au fost folosite la inserarea de valori (dacă le doriți sortate, atunci folosiți sorted(d)
în locul instrucțiunii anterioare). Pentru a determina dacă o anumită cheie a fost deja inclusă în dicționar, întrebuințați cuvântul-cheie in
.
Iată un exemplu succint de utilizare a dicționarului:
>>> telefon_interior = {'jack': 4098, 'sape': 4139}
>>> telefon_interior['guido'] = 4127
>>> telefon_interior
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> telefon_interior['jack']
4098
>>> del telefon_interior['sape']
>>> telefon_interior['irv'] = 4127
>>> telefon_interior
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(telefon_interior)
['jack', 'guido', 'irv']
>>> sorted(telefon_interior)
['guido', 'irv', 'jack']
>>> 'guido' in telefon_interior
True
>>> 'jack' not in telefon_interior
False
Constructorul dict()
produce dicționare de date direct din secvențe de perechi cheie-valoare:
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}
În plus, comprehensiunile de dicționare pot fi întrebuințate la a crea dicționare de date din expresii arbitrare folosite de post de chei și de valori:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
Atunci când cheile sunt simple șiruri de caractere este mai convenabil, uneori, să specificăm perechile dicționarului folosind argumente cuvânt-cheie:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
5.6. Tehnici de iterare¶
Atunci când iterăm de-a lungul unui dicționar, cheia și valoarea corespunzătoare ei pot fi capturate simultan cu ajutorul metodei items()
.
>>> cavaleri = {'Gallahad': 'cel fără de prihană', 'Robin': 'cel viteaz'}
>>> for k, v in cavaleri.items():
... print(k, v)
...
Gallahad cel fără de prihană
Robin cel viteaz
Atunci când iterăm de-a lungul unei secvențe, indicele de poziție și valoarea corespunzătoare lui pot fi capturate simultan cu ajutorul funcției enumerate()
.
>>> for i, v in enumerate(['x', 'și', 'zero']):
... print(i, v)
...
0 x
1 și
2 zero
Pentru a itera de-a lungul a două sau mai multe secvențe în același timp, elementele acestora pot fi împerecheate cu ajutorul funcției zip()
.
>>> întrebări = ['numele', 'țelul', 'stindardul tău']
>>> răspunsuri = ['Lancelot', 'Sfântul Potir', 'cel albastru']
>>> for q, a in zip(întrebări, răspunsuri):
... print('Care este {0} tău? Este {1}.'.format(q, a))
...
Care este numele tău? Este Lancelot.
Care este țelul tău? Este Sfântul Potir.
Care este stindardul tău? Este cel albastru.
Pentru a itera de-a lungul unei secvențe în sens invers, mai întâi precizați secvența în ordinea ei obișnuită după care apelați funcția reversed()
.
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
Ca să iterați de-a lungul unei secvențe în ordinea sortării ei, utilizați funcția sorted()
care va returna o listă nouă, sortată, lăsând nemodificată lista inițială.
>>> coș = ['măr', 'portocală', 'măr', 'pară', 'portocală', 'banană']
>>> for i in sorted(coș):
... print(i)
...
banană
măr
măr
pară
portocală
portocală
Aplicând set()
asupra unei secvențe îi eliminăm duplicatele. Întrebuințarea lui sorted()
în combinație cu set()
asupra unei secvențe constituie o modalitate idiomatică de a itera de-a lungul elementelor unicat ale secvenței în ordinea sortării acesteia.
>>> coș = ['măr', 'portocală', 'măr', 'pară', 'portocală', 'banană']
>>> for f in sorted(set(coș)):
... print(f)
...
banană
măr
pară
portocală
Este tentant, uneori, să modificăm o listă în timp ce iterăm de-a lungul ei; cu toate acestea, este adesea mai simplu și mai sigur să creăm o listă nouă în loc să o modificăm pe cea inițială.
>>> import math
>>> date_brute = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> date_filtrate = []
>>> for valoarea in date_brute:
... if not math.isnan(valoarea):
... date_filtrate.append(valoarea)
...
>>> date_filtrate
[56.2, 51.7, 55.3, 52.5, 47.8]
5.7. Mai multe despre expresiile de control¶
Condițiile de îndeplinit (sau expresiile condiționale ori expresiile de control) conținute în instrucțiunile while
și if
pot folosi orice fel de operatori nu doar pe cei de comparație (relaționali).
Operatorii de comparație in
și not in
realizează teste de apartenență care determină dacă o valoare se află (sau nu se află) într-un container. Operatorii is
și is not
compară două obiecte pentru a stabili dacă ele sunt, cu adevărat, același obiect. Toți operatorii de comparație au aceeași prioritate, aceasta fiind mai mică decât cea a indiferent cărui operator numeric.
Operațiile de comparație pot fi înlănțuite. De exemplu, a < b == c
va testa dacă a
este (strict) mai mic decât b
și dacă b
este egal cu c
.
Expresiile condiționale pot fi combinate folosind operatorii booleeni and
și or
, iar rezultatul unei comparații (ori valoarea de adevăr a oricărei alte expresii booleene) poate fi negat cu not
. Acești operatori booleeni au priorități mai mici decât cele ale operatorilor de comparație; dintre ei, not
are prioritatea cea mai mare iar or
pe cea mai mică, astfel că expresia A and not B or C
este echivalentă cu (A and (not B)) or C
. Ca de obicei, parantezele rotunde pot fi întrebuințate la construcția compoziției dorite a oricărei expresii.
Operatorii booleeni and
și or
fac parte din așa-numiții operatori de scurtcircuit: argumentele (operanzii) lor se evaluează de la stânga către dreapta iar evaluarea se oprește de îndată ce rezultatul este determinat. De exemplu, dacă expresiile A
și C
sunt adevărate iar B
este falsă, atunci evaluarea expresiei A and B and C
nu necesită evaluarea lui C
. Dacă este folosită ca valoare generală și nu ca dată booleană (valoare de adevăr), valoarea returnată de un operator de scurtcircuit este ultimul argument evaluat.
Este posibil să atribuim rezultatul unei operații de comparare ori pe cel al evaluării altei expresii booleene unei variabile. De exemplu,
>>> șirul1, șirul2, șirul3 = '', 'Trondheim', 'Hammer Dance'
>>> nenul = șirul1 or șirul2 or șirul3
>>> nenul
'Trondheim'
Atenție la faptul că în Python, spre deosebire de C, atribuirile din interiorul expresiilor trebuie realizate folosind în mod explicit operatorul morsă :=
. Această restricție ne permite să evităm un tip de dificultăți întâlnite în mod obișnuit la programarea în C: situația în care tastăm =
într-o expresie în care intenționam să introducem ==
.
5.8. Comparând secvențe și alte tipuri de obiecte¶
Obiectele secvență se compară, de obicei, cu alte obiecte de același tip secvență. Operația de comparare folosește ordinea lexicografică: mai întâi sunt comparate primul element al unuia din cele două obiecte cu primul element al celuilalt obiect, iar dacă acestea diferă unul de celălalt, atunci diferența dintre ele determină rezultatul comparației; în schimb, dacă cele două elemente sunt egale, atunci se va compara cel de-al doilea element al primului obiect cu cel de-al doilea element al celuilalt obiect și așa mai departe, până când vom ajunge la finalul (măcar) unuia dintre obiecte. Dacă se întâmplă ca elementele care sunt comparate să fie ele însele obiecte ale aceluiași tip secvență, atunci comparația lexicografică va fi continuată în mod recursiv. Dacă toate elementele celor două obiecte secvență sunt egale două câte două, atunci obiectele secvență vor fi considerate egale. Dacă unul dintre obiecte este, de la început, o sub-secvență a celuilalt obiect, atunci obiectul mai scurt este cel mai mic (dintre cele două). Ordinea lexicografică a șirurilor de caractere întrebuințează numerele de punct de cod Unicode pentru ordonarea caracterelor individuale. Iată câteva exemple de comparații între secvențe de același tip:
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
Remarcați că este permisă operația de comparare a obiectelor de tipuri diferite folosind operatorii <
sau >
cu condiția ca obiectele corespunzătoare să posede metode de comparație potrivite. De exemplu, tipurile numerice diferite se compară folosind valoarea numerică a obiectelor lor, astfel că 0 este egal cu 0.0 șamd. În caz contrar, în loc să furnizeze o ordonare arbitrară, interpretorul va ridica o excepție TypeError
.
Note de subsol