In limbajul de programare Python, tuplele reprezinta obiecte din clasa `tuple`, care au urmatoarele proprietati:

- reprezinta containere utilizate pentru stocarea unor colectii ordonate de obiecte; intr-o colectie, fiecare obiect se numeste element sau item.
- prin colectie ordonata se intelege o colectie de elemente in care fiecare element poate fi identificate si referit (utilizat), prin intermediul unui numar intreg unic, denumit index;
- pot exista si elemente identice (duplicate); acestea difera doar prin valoarea indexului care le corespunde
- fiecare element este imutabil => dupa ce este creeat, continutul obiectului `tuple` nu poate fi modificat.

Asupra obiectelor de tip `tuple` se pot opera o serie de actiuni, definite in metodele clasei `tuple`.

In [1]:

    # Exemplul 1: listarea metodelor clasei tuple
    help(tuple)

    Help on class tuple in module builtins:

    class tuple(object)
     |  tuple(iterable=(), /)
     |
     |  Built-in immutable sequence.
     |
     |  If no argument is given, the constructor returns an empty tuple.
     |  If iterable is specified the tuple is initialized from iterable's items.
     |
     |  If the argument is a tuple, the return value is the same object.
     |
     |  Methods defined here:
     |
     |  __add__(self, value, /)
     |      Return self+value.
     |
     |  __contains__(self, key, /)
     |      Return key in self.
     |
     |  __eq__(self, value, /)
     |      Return self==value.
     |
     |  __ge__(self, value, /)
     |      Return self>=value.
     |
     |  __getattribute__(self, name, /)
     |      Return getattr(self, name).
     |
     |  __getitem__(self, key, /)
     |      Return self[key].
     |
     |  __getnewargs__(self, /)
     |
     |  __gt__(self, value, /)
     |      Return self>value.
     |
     |  __hash__(self, /)
     |      Return hash(self).
     |
     |  __iter__(self, /)
     |      Implement iter(self).
     |
     |  __le__(self, value, /)
     |      Return self<=value.
     |
     |  __len__(self, /)
     |      Return len(self).
     |
     |  __lt__(self, value, /)
     |      Return self<value.
     |
     |  __mul__(self, value, /)
     |      Return self*value.
     |
     |  __ne__(self, value, /)
     |      Return self!=value.
     |
     |  __repr__(self, /)
     |      Return repr(self).
     |
     |  __rmul__(self, value, /)
     |      Return value*self.
     |
     |  count(self, value, /)
     |      Return number of occurrences of value.
     |
     |  index(self, value, start=0, stop=9223372036854775807, /)
     |      Return first index of value.
     |
     |      Raises ValueError if the value is not present.
     |
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |
     |  __new__(*args, **kwargs) from builtins.type
     |      Create and return a new object.  See help(type) for accurate signature.

### Operatii cu tuple¶

| Operatie | Python
--- | --- | ---
01. | crearea unui obiect de tip `tuple`: | `()`
|  | `tuple()`
02. | accesarea unui item: | `ob[index]`
03. | concatenare tuple: | `+`
04. | replicare tuple: | `*`
05. | copiere tuple: | `ob.copy()`
06. | iterare item-uri: | `for`
07. | operatii matematice: | `sum(ob)`
|  | `min(ob)`
|  | `max(ob)`
08. | teste egalitate: | `==` `!=`
09. | teste identitate: | `is` `is not`

ob = obiect de tip `tuple`

# 1. Crearea unui obiect de tip tuple¶

### 1.1. Metoda directa¶

Un obiect de tip `tuple` poate fi creat prin specificarea continutului sau intre paranteze rotunde.

Sintaxa:

    obiect_tuple = (item_1, item_2, ...., item_N)   # elementele sunt obiecte de orice tip

In [2]:

    # Exemplul 1: crearea unei tuple goale

    empty_tuple = ()

    print(empty_tuple)

    ()

In [3]:

    # Exemplul 2: crearea unei tuple cu obiecte de diferite tipuri

    mix_tuple = (1, 'Black', 2.5)

    print(mix_tuple)

    (1, 'Black', 2.5)

### 1.2. Utilizarea metodei `tuple()`¶

Sintaxa:

    obiect_tuple = tuple(colectie_elemente)   # genereaza un obiect tuple care contine colectia de elemente

Apelarea metodei `tuple()` determina crearea unui obiect de tip `tuple`, pe baza continutului furnizat ca argument. Metoda `tuple()` se comporta ca un constructor de clasa.

In [4]:

    # Exemplul 3: crearea unei tuple dintr-un obiect lista

    a_tuple = tuple(list(range(5)))

    print(a_tuple)

    (0, 1, 2, 3, 4)

### 1.3. Utilizarea metodei list-comprehension¶

Reprezinta o tehnica avansata, care genereaza un obiect tuple pe baza unei colectii care se itereaza.

Sintaxa:

    obiect_tuple = ( expresie  for  item  in  secventa  if  conditie(item) )

unde secventa reprezinta o colectie de elemente

Executie:

- se itereaza continutul secventei; la fiecare iteratie:
  - item preia din secventa valoarea care corespunde iteratiei curente
  - se testeaza conditie(item):
    - daca conditie(item) este validata, atunci este adaugat in contintul listei rezultatul generat de expresie, apoi se trece la urmatoare iteratie.
    - daca conditie(item) nu este validata, atunci se trece la urmatoare iteratie.

# 2. Accesarea elementelor dintr-un tuple¶

### 2.1. Utilizarea operatorului subscript `[]`¶

Un tuple reprezinta o colectie de elemente ordonate secvential (o secventa), in care fiecare element este identificat si referit pe bana unui numar intreg unic, denumit index.

- un element intr-un tuple poate fi accesat prin intermediul operatorului subscript `[]`
- intr-un tuple, elementele sunt indexate in mod identic ca in cazul obiectelor de tip lista

Sintaxa:

    obiect_tuple[index]    # returneaza elementul cu indexul precizat;
                              # index trebuie sa fie un intreg

Observatie:

- Daca index nu apartine tuplei, atunci utilizarea operatorului subscript `[]` lanseaza un mesaj de eroare de tipul `IndexError`. Pentru evitarea acestui tip de eroare este necesar ca, inainte de a accesa un element intr-un tuple, sa se verifice existenta indexului prin care acesta este referit.

### 2.2. Utilizarea operatorului de feliere (slice operator)¶

Este utilizat pentru returnarea unei subtuple, care contine elemente din tuple-ul original.

Sintaxa:

    obiect_tuple[start:stop:step]    # returneaza un subtuple, care contine
                                        # elementele din tuple-ul original, care au
                                        # indexul in intervalul [start,stop-1],
                                        # cu un pas precizat in campul step

- Daca step are o valoare pozitiva, atunci indexul precizat in campul start trebuie sa refere un element ordonat inaintea elementului referit de indexul stop; in acest caz, in subtuple-ul returnat, elementele sunt ordonate in sens crescator, dpdv. a valorii indexului.
- Daca step are o valoare negativa, atunci indexul precizat in campul start trebuie sa refere un element ordonat dupa elementului referit de indexul stop; in acest caz, in subtuple-ul returnat, elementele sunt ordonate in sens descrescator, dpdv. a valorii indexului.

#### Variante de utilizare a operatorului de feliere:¶

    obiect_tuple[:stop]    # returneaza un subtuple care contine
                              # toate elementele tuple-ului
                              # care au indexul in intervalul [0,stop-1]

    obiect_tuple[start:]    # returneaza un subtuple care contine
                               # toate elementele tuple-ului,
                               # care au indexul mai mare sau egal cu start

    obiect_tuple[::step]   # returneaza un subtuple care contine
                              # toate elementele tuple-ului,
                              # cu pasul precizat in campul step

# 3. Setarea valorii elementelor dintr-un tuple¶

Deoarece un obiect de tip `tuple` este imutabil, continutul sau nu se poate modifica dupa ce obiectul este creat. Din acest motiv, operatia de setare a valorii unui element al obiectului de tip `tuple` nu este permisa.

In [5]:

    # Exemplul 4: setarea unui element al unui tuple - nu este permisa

    a_tuple = tuple(list(range(5)))

    a_tuple[0] = 100      # operatie nepermisa

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-5-0d07fcfd6bd9> in <module>
          3 a_tuple = tuple(list(range(5)))
          4
    ----> 5 a_tuple[0] = 100      # operatie nepermisa

    TypeError: 'tuple' object does not support item assignment

Observatie:
Deoarece un obiect de tip tuple este imutabil, continutul sau nu poate fi modificat dupa crearea obiectului respectiv. Din acest motiv, urmatoarele operatii, care ar determina modificarea continutului obiectului tuple, nu sunt permise:

- adaugarea de elemente noi in obiectul tuple
- extinderea obiectului de tip tuple
- stergerea unui element din obiectul tuple
- stergerea continutului obiectului tuple
- inversarea continutului obiectului tuple
- sortarea continutului obiectului tuple

# 6. Concatenarea tuplelor¶

Prin concatenarea unor obiecte tuple, se genereaza un nou obiect de tip tuple, care contine elementele tuplelor concatenate. Concatenarea tuplelor este realizata pe baza operatorului de concatenare `+`.

Sintaxa:

    tuple_nou = tuple_1 + tuple_2 + ...       # creaza un tuple nou,
                                                 # care contine elementele tuplelor concatenate

# 7. Multiplicarea (replicarea) tuplelor¶

Similar operatiei de concatenare, prin multiplicare se genereaza un nou tuple, din continutul tuple-ului original, replicat de un numar intreg de ori. Multiplicarea tuplelor este realizata pe baza operatorului de multiplicare `*`.

Sintaxa:

    tuple_noua = tuple_original * n        # creaza un tuple nou,
                                              # care contine elementele tupleului orignal,
                                              # replicate de n ori
                                              # n trebuie sa fie un numar intreg

# 8. Contorizarea numarului de aparitii ale unui element intr-un tuple¶

Se realizeaza pe baza metodei `count()`, furnizata de clasa `tuple`.

Sintaxa:

    obiect_tuple.count(item)        # returneaza numarul de aparitii ale
                                       # item-ului in tuple [0, len(obiect_tuple)]

# 9. Cautarea indexului unui element¶

Se realizeaza pe baza metodei `index()`, furnizata de clasa `tuple`.

Sintaxa:

    obiect_tuple.index(item, start, stop)    # returneaza indexul itemului
                                                # start, stop sunt parametri optionali,
                                                # care seteaza domeniul de cautare al indexului

Observatie:

- In cazul item-urilor duplicate, returneaza indexul primei aparitii a item-ului in tuple.
- In cazul in care elementul furnizat ca argument metodei `index()` nu exista in tuple, la executia metodei se lanseaza un mesaj de eroare de tipul `ValueError`. Pentru evitarea acestei erori, se recomanda ca inainte de a cauta indexul elementului, sa se verifice apartenenta sa la tuple-ul respectiv, prin utilizarea operatorului de apartenenta `in`.

# 10. Copierea tuplelor¶

Limbajul Python furnizeaza 2 tehnici care pot fi utilizate pentru copierea unui tuple, care determina rezultate diferite:

- shallow copy = copia tuple-ului reprezinta un alias al tuple-ului original, ambele tuple reprezentand acelasi obiect; in acest caz, oricare modificare a continutului copiei, modifica si continutul originalulului; aceasta tehnica este implementata pe baza operatorului de atribuire `=`.
- deep copy = copia tuple-ului reprezinta un obiect diferit fata de tuple-ului original, dar cu acelasi continut; in acest caz, modificarile operate in continutului copiei, nu modifica continutul originalulului; aceasta tehnica este implementata pe baza metodei `copy()` furnizate de catre clasa `list`.

Sintaxa "shallow copy":

    tuple_copie = tuple_original                # tuple_copie este acelasi obiect
                                                   # ca si tuple_original

Sintaxa "deep copy":

    tuple_copie = tuple_original.copy()         # tuple_copie reprezinta un obiect
                                                   # diferit fata de tuple_original
                                                   # dar cu acelasi continut

# 11. Iterarea elementelor unui tuple¶

Este realizata pe baza buclei `for`.

# 12. Operatii matematice cu elementele unui tuple¶

Python furnizeaza diferite functii matematice care se pot aplica asupra continutului obiectelor `tuple` care contin elemente numerice, in mod identic cu cazul obiectelor lista, care contin elemente numerice.

# 17. Testarea egalitatii obiectelor `tuple`¶

Se realizeaza pe baza operatorilor relationali `==` si `!=`. Operatorii relationali conecteaza obiectele `tuple` testate in expresii booleene, care sunt evaluate `True` sau `False`, in functie de continutul obiectelor `tuple`. Continutul a doua obiectel `tuple` este considerat egal, daca cele doua obiecte `tuple` au elemente de valori egale, ordonate in mod identic.

# 18. Testarea identitatii obiectelor `tuple`¶

Se realizeaza pe baza operatorilor de identitate `is` si `is not`. Operatorii de identitate conecteaza obiectele `tuple` testate in expresii booleene, in functie de relatia in care se afla cele 2 obiecte: acelasi obiect vs. obiecte diferite.