Limbajul de programare Python furnizeaza o serie de structuri de date predefinite, numite si containere. Principalele structuri de date predefinite in limbajul Python sunt urmatoarele:

- liste
- tuple
- seturi
- dictionare

Alte structuri de date predefinite in limbajul Python sunt (care nu sunt acoperite in acest curs):

- string-uri
- frozenset

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

- listele 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;
- intr-o lista, pot exista si elemente duplicate; elementele duplicate difera doar prin valoarea indexului care le corespunde
- intr-o lista, fiecare element este mutabil = dupa crearea listei, continutul (starea) elementului se poate modifica.

Asupra obiectelor de tip `list` se pot efectua o serie de operatii, definite in cadrul metodelor clasei `list`. Metodele sunt similare functiilor, dar sunt definite in cadrul unei clase.

In [1]:

    # Exemplul 0: listarea metodelor clasei list prin utilizarea functiei Tab-completion
    help(list)

    Help on class list in module builtins:

    class list(object)
     |  list(iterable=(), /)
     |
     |  Built-in mutable sequence.
     |
     |  If no argument is given, the constructor creates a new empty list.
     |  The argument must be an iterable if specified.
     |
     |  Methods defined here:
     |
     |  __add__(self, value, /)
     |      Return self+value.
     |
     |  __contains__(self, key, /)
     |      Return key in self.
     |
     |  __delitem__(self, key, /)
     |      Delete self[key].
     |
     |  __eq__(self, value, /)
     |      Return self==value.
     |
     |  __ge__(self, value, /)
     |      Return self>=value.
     |
     |  __getattribute__(self, name, /)
     |      Return getattr(self, name).
     |
     |  __getitem__(...)
     |      x.__getitem__(y) <==> x[y]
     |
     |  __gt__(self, value, /)
     |      Return self>value.
     |
     |  __iadd__(self, value, /)
     |      Implement self+=value.
     |
     |  __imul__(self, value, /)
     |      Implement self*=value.
     |
     |  __init__(self, /, *args, **kwargs)
     |      Initialize self.  See help(type(self)) for accurate signature.
     |
     |  __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).
     |
     |  __reversed__(self, /)
     |      Return a reverse iterator over the list.
     |
     |  __rmul__(self, value, /)
     |      Return value*self.
     |
     |  __setitem__(self, key, value, /)
     |      Set self[key] to value.
     |
     |  __sizeof__(self, /)
     |      Return the size of the list in memory, in bytes.
     |
     |  append(self, object, /)
     |      Append object to the end of the list.
     |
     |  clear(self, /)
     |      Remove all items from list.
     |
     |  copy(self, /)
     |      Return a shallow copy of the list.
     |
     |  count(self, value, /)
     |      Return number of occurrences of value.
     |
     |  extend(self, iterable, /)
     |      Extend list by appending elements from the iterable.
     |
     |  index(self, value, start=0, stop=9223372036854775807, /)
     |      Return first index of value.
     |
     |      Raises ValueError if the value is not present.
     |
     |  insert(self, index, object, /)
     |      Insert object before index.
     |
     |  pop(self, index=-1, /)
     |      Remove and return item at index (default last).
     |
     |      Raises IndexError if list is empty or index is out of range.
     |
     |  remove(self, value, /)
     |      Remove first occurrence of value.
     |
     |      Raises ValueError if the value is not present.
     |
     |  reverse(self, /)
     |      Reverse *IN PLACE*.
     |
     |  sort(self, /, *, key=None, reverse=False)
     |      Stable sort *IN PLACE*.
     |
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |
     |  __new__(*args, **kwargs) from builtins.type
     |      Create and return a new object.  See help(type) for accurate signature.
     |
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |
     |  __hash__ = None

### Operatii cu liste¶

| Operatie | Python
--- | --- | ---
01. | crearea unui obiect de tip `list`: | `[]`
|  | `list()`
02. | accesarea unui item: | `lista[index]`
03. | setarea valorii unui item: | `lista[index] = value`
04. | introducerea unui item nou: | `lista.append(item)`
|  | `lista.insert(item)`
05. | extinderea unei liste: | `lista.extend(newList)`
06. | concatenare liste: | `+`
07. | replicare liste: | `*`
08. | stergerea unui item: | `lista.remove(item)`
|  | `lista.pop(index)`
|  | `del(lista[index])`
09. | stergerea tuturor itemu-urilor: | `lista.clear()`
10. | contorizarea aparitiilor unui item: | `lista.count(item)`
11. | cautare index item: | `lista.index(item)`
12. | copierea listelor: | `lista.copy()`
13. | inversarea ordinii item-urilor: | `lista.reverse()`
14. | sortarea itemurilor: | `lista.sort()`
|  | `sorted(lista)`
15. | iterare item-uri: | `for`
16. | operatii matematice: | `sum(lista)`
|  | `min(lista)`
|  | `max(lista)`
17. | teste egalitate: | `==` `!=`
18. | teste identitate: | `is` `is not`

lista = obiect de tip `list`

# 1. Crearea unui obiect de tip lista¶

### 1.1. Metoda directa¶

Un obiect de tip lista poate fi creat prin specificarea continutului listei intre paranteze drepte.

Sintaxa:

    obiect_lista = [item_1, item_2, ...., item_N]   # elementele sunt obiecte de orice tip

In [2]:

    # Exemplul 1: crearea unei liste goale

    empty_list = []

    # Exemplul 2: crearea unei liste de numere intregi

    int_list = [10, 4, 22]

    # Exemplul 3: crearea unei liste de string-uri

    str_list = ['Black', 'White', 'Red', 'Blue']

    # Exemplul 4: crearea unei liste de obiecte mixte

    mixed_list = ['Moto', 'black_cat', 9.5]

    # Exemplul 5: crearea unei liste de liste - nu reprezinta o matrice!

    list_of_lists = [[1,2,3], str_list]

    # afisare rezultate

    print('empty_list = ', empty_list, end = '\n'*2)
    print('int_list = ', int_list, end = '\n'*2)
    print('str_list = ', str_list, end = '\n'*2)
    print('mixed_list = ', mixed_list, end = '\n'*2)
    print('list_of_lists = ', list_of_lists, end = '\n'*2)

    empty_list =  []

    int_list =  [10, 4, 22]

    str_list =  ['Black', 'White', 'Red', 'Blue']

    mixed_list =  ['Moto', 'black_cat', 9.5]

    list_of_lists =  [[1, 2, 3], ['Black', 'White', 'Red', 'Blue']]

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

Sintaxa:

    obiect_lista = list(colectie_elemente)   # genereaza un obiect lista
                                             # care contine colectia de elemente

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

In [4]:

    # Exemplul 6: crearea unei liste de caractere, apeland la constructorul list()

    char_list = list('Robot')

    # afisare rezultat

    print('char_list = ', char_list, end = '\n'*2)

    char_list =  ['R', 'o', 'b', 'o', 't']

In [5]:

    # Exemplul 7: crearea unei liste de intregi, pe baza functiei range()

    step_list = list(range(1,10,2))

    # afisare rezultat

    print('step_list = ', step_list, end = '\n'*2)

    step_list =  [1, 3, 5, 7, 9]

### 1.3. Utilizarea metodei list-comprehension¶

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

Sintaxa:

    obiect_lista = [ 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.

In [6]:

    # Exemplul 8: crearea unei liste pe baza unei functii matematice

    odd_squares_list = [i**2 for i in range(10) if i%2]

    # afisare rezultat

    print('odd_squares_list = ', odd_squares_list)

    odd_squares_list =  [1, 9, 25, 49, 81]

# 2. Accesarea elementelor dintr-o lista¶

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

O lista 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-o lista poate fi accesat prin intermediul operatorului subscript `[]`
- intr-o lista, elementele sunt indexate in 2 moduri:
  - crescator:
    - primul element dintr-o lista este indexat cu valoarea 0 , iar urmatoarele elemente cu valori incrementate cu 1
    - ultimul element dintr-o lista este indexat cu valoarea `len(lista)-1` unde `len()` reprezinta functia care returneaza lungimea listei, care este egala cu numarul de elemente al listei
  - descrecator:
    - ultimul element al listei este indexat si prin -1, iar urmatoarele elemente cu valori decrementate cu 1
    - ultimul element dintr-o lista este indexat cu valoarea 0

Exemplu de indexare elemente intr-o lista:

continut: n itemuri | item 0 | item 1 | ........ | item n-2 | item n-1
--- | --- | --- | --- | --- | ---
index crescator | 0 | 1 | ... | n-2 | n-1
index descrescator | -n | -n + 1 | ... | -2 | -1

Sintaxa:

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

Observatie:

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

In [7]:

    # Exemplul 10: accesarea elementelor unei liste pe baza operatorului subscript;

    char_list = list('abcdefgh')

    MAX = len(char_list)

    # afisare rezultate

    print('MAX = ', MAX)

    print(char_list[0],  char_list[1],  char_list[ MAX-1 ])  # char_list[MAX] genereaza o eroare de tipul IndexError

    print(char_list[-1], char_list[-2], char_list[2])

    MAX =  8
    a b h
    h g c

Observatie:

- In cazul in care elementele unei liste sunt reprezentate de alte containere (de exemplu, liste de liste), un element al listei se acceseaza la fel ca in cazul listelor simple, prin utilizarea operatorului subscript `[]`. Trebuie tinut cont insa, ca in acest caz, prin utilizarea operatorului subscript, se acceseaza intregul container.

In [8]:

    # Exemplul 11: accesarea elementelor unei liste de liste

    list_of_lists = [ [1,2,3], [4,5,6], [7,8,9]]

    # accesare elemente lista

    list_0 = list_of_lists[0]
    list_1 = list_of_lists[1]
    list_2 = list_of_lists[2]

    # afisare rezultate

    print('list_of_lists =', list_of_lists, end = '\n'*2)
    print('list_of_lists[0] =', list_0)
    print('list_of_lists[1] =', list_1)
    print('list_of_lists[2] =', list_2)

    list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

    list_of_lists[0] = [1, 2, 3]
    list_of_lists[1] = [4, 5, 6]
    list_of_lists[2] = [7, 8, 9]

Observatie:

- In cazul in care elementele unei liste sunt reprezentate de alte containere (de exemplu, liste de liste), un element al unui element al listei se acceseaza prin utilizarea operatorului dublu subscript `[][]`, in care indexul din primul operator acceseaza elementul listei, iar al 2lea index acceseaza elementul elementului listei.

In [9]:

    # Exemplul 12: accesarea elementelor elementului unei liste

    list_of_lists = [ [i*j for j in range(3)] for i in range(3)]

    # accesare elemente lista

    item_01 = list_of_lists[0][1]
    item_11 = list_of_lists[1][1]
    item_21 = list_of_lists[2][1]

    # afisare rezultate

    print('list_of_lists =', list_of_lists, end = '\n'*2)
    print('item[0][1] =', item_01)
    print('item[1][1] =', item_11)
    print('item[2][1] =', item_21)

    list_of_lists = [[0, 0, 0], [0, 1, 2], [0, 2, 4]]

    item[0][1] = 0
    item[1][1] = 1
    item[2][1] = 2

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

Este utilizat pentru returnarea unei subliste, care contine elemente din lista originala.

Sintaxa:

    lista[start:stop:step]    # returneaza o sublista,
                                 # care contine elementele din lista originala, 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 crescator inaintea elementului referit de indexul stop; in acest caz, in sublista returnata, 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 descrescator inaintea elementului referit de indexul stop; in acest caz, in sublista returnata, elementele sunt ordonate in sens descrescator, dpdv. a valorii indexului.

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

    lista[:stop]    # returneaza o sublista care contine toate elementele listei
                       # care au indexul in intervalul [0,stop-1]

    lista[start:]   # returneaza o sublista care contine toate elem. listei,
                       # care au indexul mai mare sau egal cu start

    lista[::step]   # returneaza o sublista care contine toate elementele listei,
                       # cu pasul precizat in campul step

In [11]:

    char_list = [chr(i) for i in range(97,123)]

    # Exemplul 13: accesarea unei subliste, care contine toate elementele unei liste, pana la un index precizat
    sublist_1 = char_list[:10]

    # Exemplul 14: accesarea unei subliste, care contine toate elementele unei liste, incepand cu un index precizat
    sublist_2 = char_list[10:]

    # Exemplul 15: accesarea unei subliste, care contine, ordonate crescator, toate elementele unei liste, cu un pas precizat
    sublist_3 = char_list[::5]

    # Exemplul 16: accesarea unei subliste, care contine ordonate crescator toate elementele unei liste care au indexul
    # intr-un interval precizat, cu un pas precizat
    sublist_4 = char_list[5:20:5]

    # Exemplul 17: accesarea unei subliste, care contine, ordonate descrescator, toate elementele unei liste, cu un pas precizat
    sublist_5 = char_list[::-1]

    # Exemplul 18: accesarea unei subliste, care contine ordonate descrescator toate elementele unei liste care au indexul
    # intr-un interval precizat, cu un pas precizat
    sublist_6 = char_list[20:5:-5]

    END = '\n'*2;

    # afisare rezultate

    print('char_list = ', char_list, end = END);
    print('sublist_1 = ', sublist_1, end = END); print('sublist_2 = ', sublist_2, end = END)
    print('sublist_3 = ', sublist_3, end = END); print('sublist_4 = ', sublist_4, end = END);
    print('sublist_5 = ', sublist_5, end = END); print('sublist_5 = ', sublist_6)

    char_list =  ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

    sublist_1 =  ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

    sublist_2 =  ['k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

    sublist_3 =  ['a', 'f', 'k', 'p', 'u', 'z']

    sublist_4 =  ['f', 'k', 'p']

    sublist_5 =  ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

    sublist_5 =  ['u', 'p', 'k']

Observatie:

- In cazul in care elementele unei liste sunt reprezentate de alte containere (de exemplu, liste de liste), operatorul de feliere se accepta doar in interiorul unui singur operator subscript. Astfel, daca se utilizeaza operatorul de feliere in ambii operatori subscript, atunci operatorul de feliere aplicat containerelor accesate ale listei este ignorat.

In [12]:

    # Exemplul 19: accesarea elementelor elementului unei liste

    list_of_lists = [ [j for j in range(4*i, 4*i + 4)] for i in range(4)]

    print('list_of_lists = ')
    for i in range(len(list_of_lists)):
        print(list_of_lists[i])

    s_list_0 = list_of_lists[0:2]
    s_list_1 = list_of_lists[0:2][0:2] # !!! al 2lea operator subscript este ignorat !!!
    s_list_2 = list_of_lists[0][0:2]
    s_list_3 = list_of_lists[1][0:2]

    print('\ns_list_0 =', s_list_0)
    print('s_list_1 =', s_list_1)
    print('s_list_2 =', s_list_2)
    print('s_list_3 =', s_list_3)

    list_of_lists =
    [0, 1, 2, 3]
    [4, 5, 6, 7]
    [8, 9, 10, 11]
    [12, 13, 14, 15]

    s_list_0 = [[0, 1, 2, 3], [4, 5, 6, 7]]
    s_list_1 = [[0, 1, 2, 3], [4, 5, 6, 7]]
    s_list_2 = [0, 1]
    s_list_3 = [4, 5]

# 3. Setarea valorii elementelor dintr-o lista¶

Deoarece obiectele de tip `list` sunt mutabile, continutul sau se poate modifica, dupa crearea listei respective. Aceasta caracteristica permite modificarea valorii unui element identificat prin intermediul unui index. Din acest punct de vedere, un obiect de tip `list` poate fi interpretat ca un container, care contine o colectie de subcontainere, care pot stoca dinamic obiecte de diferite tipuri. Prin caracterul dinamic se ofera posibilitatea modificarii obiectului stocat in subcontainer in oricare punct al executiei codului sursa.

Pentru setarea valorii unui element dintr-o lista, se utilizeaza operatorul subscript `[]`.

Sintaxa:

    lista[index] = valoarea_noua   # seteaza valoarea elementului referit de index la valoare_noua

Observatie: In cazul in care index nu apartine listei, la executia instructiunii este lansat un mesaj de eroare de tipul `IndexError`. Pentru evitarea acestui tip de eroare, se recomanda ca, inainte de a seta valoarea unui element dintr-o lista, sa se verifice existenta indexului utilizat la referirea elementului.

In [13]:

    # Exemplul 20: setarea unui element al unei liste la o noua valoare

    END = '\n'*2

    zero_list = [0 for i in range(5)]
    print('zero_list = ', zero_list, end = END)

    middle = len(zero_list)//2      # // returneaza valoarea intreaga a impartirii

    zero_list[middle] = 1

    print('zero_list = ', zero_list, end = END)

    zero_list =  [0, 0, 0, 0, 0]

    zero_list =  [0, 0, 1, 0, 0]

In cazul in care se intentioneaza modificarea valorilor elementelor dintr-o sublista a unei liste originale, se utilizeaza operatorul de feliere.

Sintaxa:

    lista[start:stop:step] = valori_noi   # seteaza valorile elementelor dintr-o sublista
                                             # la valorile noi

Observatie:
Daca lungimea (numarul de elemente) sublistei generate prin aplicarea operatorului de feliere nu este egal cu numarul de valori noi atunci:

- daca lungime sublista > numar valori => se modifica doar valorile primelor elemente din sublista; lungimea listei originale nu se modifica
- daca lungime sublista < numar valori => se modifica valorile tuturor elementelor din sublista si se insereaza in lista originala, incepand cu indexul care corespunde valorii 'stop', restul valorilor, prin translatarea elementelor din lista originala, care nu intra in sublista, la indecsi de valori mai mari; lungimea listei originale creste

In [14]:

    # Exemplul 21: setarea elementelor dintr-o sublista la valori noi
    END = '\n'*2

    zero_list = [0 for i in range(5)]

    print('zero_list = ', zero_list, end = END)

    zero_list[1:-1:1] = [1 for i in range(len(zero_list)-2)]

    print('zero_list = ', zero_list, end = END)

    zero_list =  [0, 0, 0, 0, 0]

    zero_list =  [0, 1, 1, 1, 0]

In [15]:

    # Exemplul 22: setarea elementelor dintr-o sublista la valori noi

    ROWS = 6
    COLS = 6

    # generarea unei liste de liste
    zero_lists = [ [0 for i in range(COLS)] for i in range(ROWS)]

    for i in range(ROWS):
        print(zero_lists[i])

    print('', end = '\n'*2)

    for i in range(ROWS):
        if 1 <= i <= 4:
            zero_lists [i][1:5] = [ 1 for i in range(4)]

    for i in range(ROWS):
        print(zero_lists[i])

    [0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0]
    [0, 0, 0, 0, 0, 0]

    [0, 0, 0, 0, 0, 0]
    [0, 1, 1, 1, 1, 0]
    [0, 1, 1, 1, 1, 0]
    [0, 1, 1, 1, 1, 0]
    [0, 1, 1, 1, 1, 0]
    [0, 0, 0, 0, 0, 0]

# 4. Introducerea unui element nou in lista¶

Pentru adaugarea unui nou element intr-o lista, clasa `list` furnizeaza 2 metode si anume metoda `append()`, care adauga elementul la finalul listei (dupa indexul maxim, in lista orignala), respectiv metoda `insert()`, care insereaza elementul la indexul precizat, mutand elementele care, in lista originala, au index mai mare sau egal cu cel precizat, pe o noua pozitie, care corespunde vechiului index, incrementat cu 1. Ambele metode determina cresterea lungimii listei originale cu 1.

### Adaugarea unui element la finalul listei originale¶

Se realizeaza apeland metoda `append()`.

Sintaxa:

    lista.append(item_nou)         # adauga in lista originala item_nou, la finalul listei
                                       # lista originala se modifica, lungimea ei crescand cu 1,
                                       # nu returneaza noua lista (returneaza valoarea None).

In [16]:

    # Exemplul 23: adaugarea unui element nou, la finalul listei
    import time

    DELAY = 1
    int_list = []

    time.sleep(DELAY)

    print(int_list)

    for i in range(5):
        time.sleep(DELAY)
        int_list.append(i)
        print(int_list)

    []
    [0]
    [0, 1]
    [0, 1, 2]
    [0, 1, 2, 3]
    [0, 1, 2, 3, 4]

### Inserarea unui element in lista originala, la o anumita pozitie precizata¶

Se realizeaza apeland metoda `insert()`.

Sintaxa:

    lista.insert(index, item_nou)   # insereaza in lista originala item_nou,
                                       # la pozitia precizata de index
                                       # lista originala se modifica, lungimea ei crescand cu 1,
                                       # nu returneaza o noua lista (returneaza valoarea None)

                                       # elementele care au indexul original
                                       # mai mare sau egal cu index
                                       # sunt mutate pe pozitii care corespund
                                       # unor indecsi incrementati cu 1

                                       # daca index > valoarea maxima
                                       # a indexului in lista originala, atunci
                                       # elementul este introdus la finalul listei

In [17]:

    # Exemplul 24: inserarea unui element nou, la o pozitie specificata

    import time

    DELAY = 1
    int_list = [0 for i in range(3)]

    time.sleep(DELAY)

    print(int_list)

    for index in range(5):
        time.sleep(DELAY)
        item = index+1
        int_list.insert(index, item)
        print(int_list)

    [0, 0, 0]
    [1, 0, 0, 0]
    [1, 2, 0, 0, 0]
    [1, 2, 3, 0, 0, 0]
    [1, 2, 3, 4, 0, 0, 0]
    [1, 2, 3, 4, 5, 0, 0, 0]

# 5. Extinderea unei liste cu continutul altei liste¶

O lista originala se poate extinde prin adaugarea la finalul acesteia,a continutului altei liste. In acest scop, clasa `list` furnizeaza metoda `extend()`. Prin apelarea acestei metode, nu se returneaza o noua lista; efectul consta doar in extinderea listei originale. Prin apelarea acestei metode, lungimea listei originale creste la o valoare egala cu suma lungimilor listei originale si a celei utilizate pentru extindere.

Sintaxa:

    lista.extend(alta_lista)        # extinde lista originala cu alta_lista,
                                       # adaugata la finalul listei originale
                                       # lista originala se modifica,
                                       # lungimea ei crescand cu
                                       # lungimea listei utilizate pentru extindere,
                                       # nu returneaza o noua lista (returneaza valoarea None).

In [18]:

    # Exemplul 25: extinderea unei liste

    one_list  = [1 for i in range(4)]
    tail_list = [0 for i in range(2)]

    print('before', one_list)

    one_list.extend(tail_list)

    print('after ', one_list)

    before [1, 1, 1, 1]
    after  [1, 1, 1, 1, 0, 0]

# 6. Concatenarea listelor¶

Spre deosebire de extindere, prin concatenare se genereaza o noua lista, care contine elementele listelor concatenate. Concatenarea listelor este realizata pe baza operatorului de concatenare `+`.

Sintaxa:

    lista_noua = lista_1 + lista_2 + ...     # creaza o lista noua,
                                                # care contine elementele listelor concatenate

In [19]:

    # Exemplul 26: concatenarea listelor

    a_list = list('abc')
    b_list = list('def')

    ab_list = a_list + b_list + a_list

    print(ab_list)

    ['a', 'b', 'c', 'd', 'e', 'f', 'a', 'b', 'c']

# 7. Multiplicarea (replicarea) listelor¶

Similar operatiei de concatenare, prin multiplicare se genereaza o noua lista, din continutul listei originale, replicat de un numar intreg de ori. Multiplicarea listelor este realizata pe baza operatorului de multiplicare `*`.

Sintaxa:

    lista_noua = lista_originala * n       # creaza o lista noua,
                                              # care contine elementele listei orignale,
                                              # replicate de n ori
                                              # n trebuie sa fie un numar intreg

In [20]:

    # Exemplul 27: replicarea listelor

    a_list = list(range(2))

    a_list_3 = a_list * 5

    print(a_list_3)

    [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]

# 8. Stergerea elementelor dintr-o lista¶

Stergerea unui element dintr-o lista elimina permanent elementul respectiv din continutul listei. Daca elementul sters este in interiorul listei, stergerea sa genereaza o decrementare cu 1 a indecsilor elementelor care, in lista originala au un index mai mare decat cel al elementului sters. Continutul listei originale se modifica, lungimea sa micsorandu-se cu 1.

### Stergerea din lista a unui element precizat nominal¶

Este realizata cu metoda `remove()` furnizata de clasa `list`.

Sintaxa:

    lista.remove(item)        # sterge primul element identificat prin argumentul item
                                 # din lista originala
                                 # lista originala se modifica, lungimea ei descrescand cu 1,
                                 # nu returneaza o noua lista (returneaza valoarea None)

Observatii:

1. In cazul in care elementul identificat prin argumentul item este duplicat in lista originala, metoda `remove()` sterge din lista doar elementul care are indexul cel mai mic, in ordine crescatoare.
2. In cazul in care elementul furnizat ca argument metodei `remove()` nu exista in lista, la executia metodei se lanseaza un mesaj de eroare de tipul `ValueError`. Pentru evitarea acestei erori, se recomanda ca inainte de a sterge un element dintr-o lista sa se verifice apartenenta sa la lista respectiva, prin utilizarea operatorului de apartenenta `in`.

In [21]:

    # Exemplul 28: stergerea unui element dintr-o lista

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']
    print('cars_list =', cars_list, end = '\n'*2)

    deleted_item =  'Toyota'  # 'Dacia'

    if deleted_item in cars_list:
        cars_list.remove(deleted_item)
    else:
       print('Error: deleted_item does not belong to list. Try again.')

    print('cars_list =', cars_list)

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

    cars_list = ['Subaru', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

### Stergerea din lista a unui element precizat prin index¶

Este realizata cu metoda `pop()` furnizata de clasa `list`.

Sintaxa:

    lista.pop(index)        # sterge elementul de la pozitia precizata in index
                               # index este un optional;
                               # daca index nu este precizat
                               # se sterge automat ultimul element din lista
                               # returneaza elementul sters
                               # lista originala se modifica, lungimea ei scazand cu 1

Observatie:
In cazul in care valoarea indexului nu este corecta, la executia metodei se lanseaza un mesaj de eroare de tipul `IndexError`. Una din cauzele cele mai frecvente care determina aceasta eroare consta in incercarea de a sterge un element in conditiile in care lista este goala, sau utilizarea unui index care nu apartine intervalului de valori al indecsilor elementelor listei. In acest caz, pentru evitarea erorii, se recomanda ca, inainte de a sterge un element dintr-o lista, sa se verifice daca lista este goala si totodata apartenenta indexului la intervalul de valori al indecsilor elementelor listei, prin utilizarea operatorului de apartenenta `in`.

In [22]:

    # Exemplul 29: stergerea unui element dintr-o lista; elementul este precizat prin indexul sau
    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']
    print('cars_list =', cars_list, end = '\n'*2)

    index = 8

    if cars_list !=[] and index in range(len(cars_list)) :
        cars_list.pop(index)
    else:
       print('Error: you try to delete an item from an empty list or the index you use it is to large. Try again.', end = '\n'*2)

    print('cars_list =', cars_list)

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

    Error: you try to delete an item from an empty list or the index you use it is to large. Try again.

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

In [23]:

    # Exemplul 30: stergerea automata a elementelor dintr-o lista
    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']
    print(cars_list, end = '\n'*2)

    while cars_list != []:
        deleted_item = cars_list.pop()
        print(cars_list, deleted_item, sep = ' --> ', end = '\n'*2)

    ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

    ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda'] --> Suzuki

    ['Subaru', 'Toyota', 'Honda', 'Toyota'] --> Mazda

    ['Subaru', 'Toyota', 'Honda'] --> Toyota

    ['Subaru', 'Toyota'] --> Honda

    ['Subaru'] --> Toyota

    [] --> Subaru

### Stergerea din lista a unui element apeland functia predefinita `del()`¶

Limbajul de programare Python furnizeaza pentru stergerea unui element dintr-o colectie, functia predefinita `del()`. In cazul stergerii unui element din continutul unei liste, acesta trebuie furnizat ca argument functiei `del()`. Elementul respectiv trebuie referit prin indexul sau, utilizand operatorul subscript `[]`.

Sintaxa:

    del(lista[index])        # sterge elementul de la pozitia precizata in index

Observatie - aceeasi care este valabila si pentru metoda `pop()`:

In [24]:

    # Exemplul 31: stergerea unui element dintr-o lista; elementul este precizat prin indexul sau - se apeleaza functia del()

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']
    print('cars_list =', cars_list, end = '\n'*2)

    index = 1

    if cars_list != [] and index in range(len(cars_list)) :
       del( cars_list[index] )
    else:
       print('Error: you try to delete an item from an empty list or the index you use it is to large. Try again.')

    print('cars_list =', cars_list)

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

    cars_list = ['Subaru', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

# 9. Stergerea continutului unei liste¶

Se realizeaza pe baza metodei `clear()`, furnizata de clasa `list`.

Sintaxa:

    lista.clear()           # sterge intregul continut al listei;
                               # nu returneaza (returneaza valoarea None)

In [25]:

    # Exemplul 32: stergerea continutului unei liste

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']
    print('cars_list =', cars_list, end = '\n'*2)

    cars_list.clear()

    print('cars_list =', cars_list)

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

    cars_list = []

# 10. Contorizarea numarului de aparitii a unui element intr-o lista¶

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

Sintaxa:

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

In [26]:

    # Exemplul 33: contorizarea numarului de aparitii ale unui element intr-o lista

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Honda']
    item = 'Toyota' # 'Nissan'

    num_occurences = cars_list.count(item)

    print(f'{item}: {num_occurences}')

    Toyota: 2

In [27]:

    # Exemplul 34: tabelarea numarului de aparitii ale elementelor unei liste

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Nissan', 'Mazda', 'Toyota' ,\
                 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Honda']

    print(cars_list, '\n')
    processed_items_list = []

    for item in cars_list:
        if item not in processed_items_list:
            num_of_occurences = cars_list.count(item)
            result = f'{item:6} : {num_of_occurences:3}'
            print(result)
            processed_items_list.append(item)

    ['Subaru', 'Toyota', 'Honda', 'Nissan', 'Mazda', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Honda']

    Subaru :   1
    Toyota :   3
    Honda  :   4
    Nissan :   1
    Mazda  :   2
    Suzuki :   1

# 11. Cautarea indexului unui element¶

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

Sintaxa:

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

Observatii:

- Daca item-ul furnizat ca argument este replicat in continutul listei, metoda returneaza indexul cel mai mic (prima aparitie)
- In cazul in care elementul furnizat ca argument metodei `index()` nu exista in lista, la executia metodei se lanseaza un mesaj de eroare de tipul `ValueError`. Pentru evitarea acestei erori, se recomanda ca inainte de a cauta indexul elementui, sa se verifice apartenenta sa la lista respectiva, prin utilizarea operatorului de apartenenta `in`.

In [28]:

    # Exemplul 35: returnarea indexului unui element din lista

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Honda']

    item = 'Honda' # 'Nissan'

    if item in cars_list:
        first_occurence = cars_list.index(item)
        print(f'{item}: {first_occurence}')
    else:
        print('Warning: There is no such item inside the list.')

    Honda: 2

# 12. Copierea listelor¶

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

- shallow copy = copia listei este un alias al listei originale, ambele liste 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 listei reprezinta un obiect diferit fata de lista originale, 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":

    lista_copie = lista_originala          # lista_copie = acelasi obiect
                                              # ca si lista_originala

Sintaxa "deep copy":

    lista_copie = lista_originala.copy()   # lista_copie = obiect diferit
                                              # fata de lista_originala
                                              # dar cu acelasi continut

In [29]:

    # Exemplul 36: copierea listelor - shallow copy

    original_list = ['Honda','Mazda']
    copy_list = original_list                            # shallow_copy

    copy_list.insert(0, 'Toyota')

    print('shallow_copy:')
    print(f'copy_list:      {copy_list}')
    print(f'original_list:  {original_list}\n')

    # Exemplul 37: copierea listelor - deep copy
    original_list = ['Honda','Mazda']
    copy_list = original_list.copy()                    # deep_copy

    copy_list.insert(0, 'Toyota')

    print('deep_copy:')
    print(f'copy_list:      {copy_list}')
    print(f'original_list:  {original_list}')

    shallow_copy:
    copy_list:      ['Toyota', 'Honda', 'Mazda']
    original_list:  ['Toyota', 'Honda', 'Mazda']

    deep_copy:
    copy_list:      ['Toyota', 'Honda', 'Mazda']
    original_list:  ['Honda', 'Mazda']

# 13. Inversarea elementelor din continutul listei¶

Se realizeaza pe baza metodei `reverse()`, furnizata de clasa `list`. Aceasta metoda modifica continutul listei originale (in care ordinea elementelor este inverstata), fara a returna nimic (nu genereaza o noua lista, care contine elementele listei originale in ordine inversata).

Sintaxa:

    lista.reverse()               # modifica continutul listei: inverseaza ordinea elementelor
                                     # nu returneaza nimic

In [31]:

    # Exemplul 38: inversarea elementelor listei

    a_list = list(range(10))
    print(a_list, end = '\n'*2)

    a_list.reverse()    # print(a_list[::-1])

    print(a_list)

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

# 14. Sortarea elementelor din continutul listei¶

Se realizeaza pe baza metodei `sort()`, furnizata de clasa `list`. Aceasta metoda se poate aplica doar pe liste in care toate elementele sunt de acelsi tip si modifica continutul listei originale (in care ordinea elementelor este sortata), fara a returna nimic. Metoda nu genereaza o noua lista, care contine elementele sortate ale listei originale. Prin sortare, elementele listei sunt ordonate in ordine crescatoare, de la cel mai mic, la cel mai mare.

Sintaxa:

    lista.sort(key = regula_sortare, reverse = False)
                                            # modifica continutul listei:
                                            # sorteaza elementele listei in ordine crescatoare

                                            # key = parametru optional care seteaza
                                            # criteriul de comparatie aplicat elementelor
                                            # valoarea atribuita lui key
                                            # trebuie sa fie o functie care:
                                            #        preia un singur argument
                                            #        returneaza criteriul utilizat
                                            #        in compararea elementelor

                                            # reverse = parametru optional,
                                            # setat implicit la valoarea False
                                            # reverse = True, sortarea se realizeaza
                                            # pe continutul inversat al listei

                                            # nu returneaza nimic

In [32]:

    # Exemplul 39: sortarea elementelor listei

    original_list = list('wFDJaHn')
    print(f'original_list:                  {original_list}', end = '\n'*2)

    # sortare
    original_list.sort()
    print(f'sorted_list:                    {original_list}', end = '\n'*2)

    # sortare: criteriul de comparatie = case insensitive
    original_list.sort(key = str.lower)
    print(f'case_insensitive_sorted_list:   {original_list}')

    original_list:                  ['w', 'F', 'D', 'J', 'a', 'H', 'n']

    sorted_list:                    ['D', 'F', 'H', 'J', 'a', 'n', 'w']

    case_insensitive_sorted_list:   ['a', 'D', 'F', 'H', 'J', 'n', 'w']

### Generarea unei noi liste, cu elemente sortate¶

In cazul in care se doreste generarea unei noi liste, cu acelasi continut ca al listei originale, dar cu elemente sortate, limbajul Python furnizeaza functia `sorted()`.

Sintaxa:

    lista_sortata = sorted(lista_originala, key = functie_sortare, reverse = True/False)
                                            # sorteaza elementelor listei originale
                                            # returneaza o lista noua, cu elemente sortate,
                                            # parametrii optionali key/reverse au aceeasi
                                            # utilizare ca si metoda .sort()

In [33]:

    # Exemplul 40: generarea unei noi liste, cu elemente sortate

    original_list = list('sgwterb')
    print(f'original_list: {original_list}', end = '\n'*2)

    # sortare
    sorted_list = sorted(original_list)  # functia sorted() returneaza o noua lista
    print(f'sorted_list:   {sorted_list}')

    original_list: ['s', 'g', 'w', 't', 'e', 'r', 'b']

    sorted_list:   ['b', 'e', 'g', 'r', 's', 't', 'w']

# 15. Iterarea elementelor unei liste¶

Este realizata pe baza buclei `for`.

In [34]:

    # Exemplul 41: iterarea elementelor unei liste

    chr_list = [chr(i) for i in range(97,123)]
    for c in chr_list:
        print(c)

    a
    b
    c
    d
    e
    f
    g
    h
    i
    j
    k
    l
    m
    n
    o
    p
    q
    r
    s
    t
    u
    v
    w
    x
    y
    z

In [35]:

    # Exemplul 42: iterarea elementelor unei liste de liste si afisare 2D

    ROWS = 3
    COLS = 3

    power_list = [ [j**i for j in range(1, COLS+1)] for i in range(1, ROWS+1)]

    for i in range(ROWS):
        for j in range(COLS):
            if j == COLS - 1:
                END = '\n'
            else:
                END = ' '
            print(power_list[i][j], end = END)

    1 2 3
    1 4 9
    1 8 27

## Metoda `enumerate()`¶

Metoda `enumerate()` asociaza unei colectii un counter, pe care il returneaza, care contine perechi de forma index - element.

Sintaxa:

    enumerate(colectie, start)       # returneaza un obiect din clasa "enumerate"
                                        # care asociaza un index fiecarui element al colectiei
                                        # start = argument optional; seteaza valoarea
                                        # indexului primului element;
                                        # daca start este omis, valoarea implicita este 0

#### Utilizare `enumerate` in bucle for¶

    for index_item in enumerate(colectie, start):    # in fiecare iteratie,
                                                        # index_item primeste un "tuple"
            iteratie                                    # de forma (index, element)

sau:

    for i, e in enumerate(colectie, start):     # in fiecare iteratie,
                                                   # i primeste valoarea indexului curent
                                                   # e primeste valoarea elementului curent

In [39]:

    # Exemplul 43: utilizarea metodei enumerate intr-o bucla for

    chr_list = [chr(i) for i in range(97,123)]

    for i,c in enumerate(chr_list):
        if i < 10:
            print(f' {i} : {c}')
        else:
            print(f'{i} : {c}')

     0 : a
     1 : b
     2 : c
     3 : d
     4 : e
     5 : f
     6 : g
     7 : h
     8 : i
     9 : j
    10 : k
    11 : l
    12 : m
    13 : n
    14 : o
    15 : p
    16 : q
    17 : r
    18 : s
    19 : t
    20 : u
    21 : v
    22 : w
    23 : x
    24 : y
    25 : z

# 16. Operatii matematice cu elementele listei¶

Python furnizeaza diferite functii matematice care se pot aplica asupra continutului listelor numerice. Cateva exemple de functii des utilizate sunt `sum()`, `min()`, `max()`. Se recomanda utilizarea acestora numai cu liste 1D.

Sintaxa:

    sum(lista)               # returneaza suma tuturor elementelor listei

Sintaxa:

    min(lista)               # returneaza elementul cu valoarea cea mai mica

Sintaxa:

    max(lista)               # returneaza elementul cu valoarea cea mai mare

In [40]:

    # Exemplul 44: afisarea informatiilor despre functia sum:

    help(sum)

    Help on built-in function sum in module builtins:

    sum(iterable, start=0, /)
        Return the sum of a 'start' value (default: 0) plus an iterable of numbers

        When the iterable is empty, return the start value.
        This function is intended specifically for use with numeric values and may
        reject non-numeric types.

In [41]:

    # Exemplul 45: afisarea informatiilor despre functia min:

    help(min)

    Help on built-in function min in module builtins:

    min(...)
        min(iterable, *[, default=obj, key=func]) -> value
        min(arg1, arg2, *args, *[, key=func]) -> value

        With a single iterable argument, return its smallest item. The
        default keyword-only argument specifies an object to return if
        the provided iterable is empty.
        With two or more arguments, return the smallest argument.

In [42]:

    # Exemplul 46: afisarea informatiilor despre functia max:

    help(max)

    Help on built-in function max in module builtins:

    max(...)
        max(iterable, *[, default=obj, key=func]) -> value
        max(arg1, arg2, *args, *[, key=func]) -> value

        With a single iterable argument, return its biggest item. The
        default keyword-only argument specifies an object to return if
        the provided iterable is empty.
        With two or more arguments, return the largest argument.

In [43]:

    # Exemplul 47: aplicarea unor functii matematice asupra elementelor listei

    num_list = [11, 3, 5, 3, 6, 77, 8, 33]

    s = sum(num_list)
    m = min(num_list)
    M = max(num_list)

    print(f'sum = {s}, min = {m}, max = {M}')

    sum = 146, min = 3, max = 77

# 17. Testarea egalitatii listelor¶

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

In [45]:

    # Exemplul 45: testarea egalitatii a 2 liste

    a_list = [1, 2]
    b_list = [1, 2]

    eq   = a_list == b_list     # True - sunt 2 obiecte cu acelasi continut
    n_eq = a_list != b_list     # False

    print(f'Equality:     {eq}')
    print(f'Non-equality: {n_eq}')

    Equality:     True
    Non-equality: False

# 18. Testarea identitatii listelor¶

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

In [46]:

    # Exemplul 46: testarea identitatii a 2 liste

    a_list = [1, 2]
    b_list = [1, 2]

    same   = a_list is b_list       # False - sunt obiecte diferite, chiar daca au acelasi continut
    n_same = a_list is not b_list   # True

    print(f'Identity:     {same}')
    print(f'Non-identity: {n_same}')

    Identity:     False
    Non-identity: True

### Identificatorul obiectelor create¶

Dupa ce este creat, fiecare obiect primeste un identificator unic - id. Limbajul Python furnizeaza functia `id()` care returneaza id-ul obiectului creat (calculat pe baza adresei in memorie a obiectului creat).

In [47]:

    # Exemplul 47: returnarea id-ului obiectului

    a_list = []

    id(a_list)

Out[47]:

    2207273237768

In [53]:

    # TEMA_1: eliminarea elementelor duplicate din continutul unei liste

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Suzuki']
    index_of_duplicants = []

    print(f'Initial list: {cars_list}', end = '\n'*2)

    copy_list  = cars_list.copy()

    for i in range(len(copy_list)-1):
        cars_subList = copy_list[i+1:]
        duplicated = False
        for j in range(len(cars_subList)):
            if copy_list[i] == cars_subList[j]:
                duplicated = True
                break

        if duplicated:
            index_of_duplicants.append(i)
            cars_list.remove(copy_list[i])

    print(f'First index of duplicated elements: {index_of_duplicants}', end = '\n'*2)
    print(f'Final list: {cars_list}')

    Initial list: ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Suzuki']

    First index of duplicated elements: [1, 2, 6]

    Final list: ['Subaru', 'Honda', 'Toyota', 'Mazda', 'Suzuki']

In [56]:

    # TEMA_2: returnarea valorilor indecsilor unui element din lista

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Nissan']
    print(cars_list, end = '\n'*2)

    item = 'Toyota' # 'Dacia'
    idx_list = []

    for index, current_item in enumerate(cars_list):
        if item == current_item:
            idx = cars_list.index(current_item, index)
            idx_list.append(idx)

    if idx_list == []:
        print(f'{item} does not belong to the list.')
    else:
        print(f'Index of {item}: {idx_list}')

    ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Nissan']

    Index of Toyota: [1, 4]