# 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

The list data type has some more methods. Here are all of the methods of list objects:

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 ce `a.insert(len(a), x)` este echivalentă cu `a.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ție `IndexError` 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 Tipuri secvență – 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 imutabile ș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 mutabile 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 also includes a data type for sets. A set is an unordered collection with no duplicate elements. Basic uses include membership testing and eliminating duplicate entries. Set objects also support mathematical operations like union, intersection, difference, and symmetric difference.

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 Tipuri de asociere – 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; șirurile de caractere și numerele pot fi oricând chei. Tuplurile sunt întrebuințabile drept chei cu condiția să nu conțină decât șiruri de caractere, numere, ori tupluri; dacă un tuplu conține vreun obiect mutabil, fie în mod direct ori indirect, atunci el nu va putea fi folosit pe post de cheie. Nu puteți utiliza listele drept chei, deoarece listele pot fi modificate pe loc cu ajutorul atriburilor la (un) index (dat), a atribuirilor la (o) tranșă (dată), respectiv a unor metode precum `append()` și `extend()`.

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.

The main operations on a dictionary are storing a value with some key and extracting the value given the key. It is also possible to delete a key:value pair with `del`. If you store using a key that is already in use, the old value associated with that key is forgotten.

Extracting a value for a non-existent key by subscripting (`d[key]`) raises a `KeyError`. To avoid getting this error when trying to access a possibly non-existent key, use the `get()` method instead, which returns `None` (or a specified default value) if the key is not in the dictionary.

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:

    >>> tel = {'jack': 4098, 'sape': 4139}
    >>> tel['guido'] = 4127
    >>> tel
    {'jack': 4098, 'sape': 4139, 'guido': 4127}
    >>> tel['jack']
    4098
    >>> tel['irv']
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: 'irv'
    >>> print(tel.get('irv'))
    None
    >>> del tel['sape']
    >>> tel['irv'] = 4127
    >>> tel
    {'jack': 4098, 'guido': 4127, 'irv': 4127}
    >>> list(tel)
    ['jack', 'guido', 'irv']
    >>> sorted(tel)
    ['guido', 'irv', 'jack']
    >>> 'guido' in tel
    True
    >>> 'jack' not in tel
    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

[1]

În alte limbaje de programare, este posibil să se returneze obiectul mutat, ceea ce va permite înlănțuirea metodelor, cum ar fi `d->insert("a")->remove("b")->sort();`.