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

- dictionarul (dictionary) reprezinta un container utilizat pentru stocarea unor colectii neordonate de obiecte de oricare tip; intr-o colectie, fiecare obiect se numeste element sau item.
- fiind o colectie neordonata, un dictionar nu organizeaza informatiile din continutul sau pe baza pozitiei ocupate de o data (pe baza unui index numeric) ci prin asocierea a doua componente, cheie - valoare
- intr-un dictionar, fiecare element are 2 componente:
  - cheie `key`
  - valoare `value`
- intr-un dictionar, un element este identificat pe baza cheii sale; din acest motiv, intr-un dictionar:
  - cheile trebuie sa fie unice - nu se permite duplicare cheilor
  - cheile trebuie sa fie obiecte imutabile: stringuri, obiecte de tip numeric sau booleean, sau tuple
  - un obiect imutabil este un obiect a carei valoare (stare) nu se poate modifica, dupa crearea sa.

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

In [2]:

    # Exemplul 0: listarea continutului clasei Dict
    help(dict)

    Help on class dict in module builtins:

    class dict(object)
     |  dict() -> new empty dictionary
     |  dict(mapping) -> new dictionary initialized from a mapping object's
     |      (key, value) pairs
     |  dict(iterable) -> new dictionary initialized as if via:
     |      d = {}
     |      for k, v in iterable:
     |          d[k] = v
     |  dict(**kwargs) -> new dictionary initialized with the name=value pairs
     |      in the keyword argument list.  For example:  dict(one=1, two=2)
     |
     |  Methods defined here:
     |
     |  __contains__(self, key, /)
     |      True if the dictionary has the specified key, else False.
     |
     |  __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.
     |
     |  __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.
     |
     |  __ne__(self, value, /)
     |      Return self!=value.
     |
     |  __repr__(self, /)
     |      Return repr(self).
     |
     |  __setitem__(self, key, value, /)
     |      Set self[key] to value.
     |
     |  __sizeof__(...)
     |      D.__sizeof__() -> size of D in memory, in bytes
     |
     |  clear(...)
     |      D.clear() -> None.  Remove all items from D.
     |
     |  copy(...)
     |      D.copy() -> a shallow copy of D
     |
     |  get(self, key, default=None, /)
     |      Return the value for key if key is in the dictionary, else default.
     |
     |  items(...)
     |      D.items() -> a set-like object providing a view on D's items
     |
     |  keys(...)
     |      D.keys() -> a set-like object providing a view on D's keys
     |
     |  pop(...)
     |      D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
     |      If key is not found, d is returned if given, otherwise KeyError is raised
     |
     |  popitem(...)
     |      D.popitem() -> (k, v), remove and return some (key, value) pair as a
     |      2-tuple; but raise KeyError if D is empty.
     |
     |  setdefault(self, key, default=None, /)
     |      Insert key with a value of default if key is not in the dictionary.
     |
     |      Return the value for key if key is in the dictionary, else default.
     |
     |  update(...)
     |      D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
     |      If E is present and has a .keys() method, then does:  for k in E: D[k] = E[k]
     |      If E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v
     |      In either case, this is followed by: for k in F:  D[k] = F[k]
     |
     |  values(...)
     |      D.values() -> an object providing a view on D's values
     |
     |  ----------------------------------------------------------------------
     |  Class methods defined here:
     |
     |  fromkeys(iterable, value=None, /) from builtins.type
     |      Create a new dictionary with keys from iterable and values set to value.
     |
     |  ----------------------------------------------------------------------
     |  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 dictionare¶

| Operatie | Python
--- | --- | ---
01. | crearea unui obiect de tip dictionar: | `{}`
|  | `dict()`
|  | `dict.fromkeys()`
02. | accesarea unui item: | `dictionar[cheie]`
|  | `dictionar.get(cheie, val_implicita)`
|  | `dictionar.setdefault()`
03. | accesarea tuturor item-urilor: | `dictionar.items()`
|  | `dictionar.keys()`
|  | `dictionar.values()`
04. | setarea valorii unui item: | `dictionar[cheie] = valoare`
| introducerea unui item nou: | `dictionar[cheie_noua] = valoare`
05. | actualizarea unui dictionar: | `dictionar.update(elemente_noi)`
06. | stergerea unui item: | `dictionar.popitem()`
|  | `dictionar.pop(cheie, valoare_returnata)`
|  | `del(dictionar[cheie])`
07. | stergerea tuturor item-urilor: | `dictionar.clear()`
08. | copierea dictionarelor: | `dictionar.copy()`
09. | iterare item-uri: | `for`
10. | teste egalitate: | `==` `!=`
11. | teste identitate: | `is` `is not`

`dictionar` = obiect de tip `dict`

# 1. Crearea unui obiect de tip dictionar¶

### 1.1. Metoda directa¶

Sintaxa:

    obiect_dict = {key_1: value_1, ...., key_n: value_n}   # elementele sunt obiecte de orice tip

Un obiect de tip dictionar poate fi creat prin specificarea continutului sau intre acolade.

In [3]:

    # Exemplul 1: crearea unui dictionar gol

    empty_dict = {}

    print(f'empty_dict: {empty_dict}')

    empty_dict: {}

In [4]:

    # Exemplul 2: crearea unui dictionar in care cheile sunt string-uri

    warrior = {'name':'John', 'weapon':'sword', 'health':10, 'enemy_status': False}

    print(f'warrior: {warrior}')

    warrior: {'name': 'John', 'weapon': 'sword', 'health': 10, 'enemy_status': False}

In [5]:

    # Exemplul 3: crearea unui dictionar in care cheile sunt numere intregi

    dec2bin = {0:'00', 1:'01', 2:'10', 3:'11'}

    print(f'dec2bin: {dec2bin}')

    dec2bin: {0: '00', 1: '01', 2: '10', 3: '11'}

In [6]:

    # Exemplul 4: crearea unui dictionar in care cheile sunt valori booleene

    statistics = {True: 100, False: 20}

    print(f'statistics: {statistics}')

    statistics: {True: 100, False: 20}

In [7]:

    # Exemplul 5: crearea unui dictionar in care cheile sunt obiecte de tip tuple:

    pixel_values = {(0,0): 120, (0,1): 110, (1,0):100, (1,1):150}

    print(f'pixel_values: {pixel_values}')

    pixel_values: {(0, 0): 120, (0, 1): 110, (1, 0): 100, (1, 1): 150}

In [8]:

    # Exemplul 6: crearea unui dictionar in care cheile sunt obiecte mixte:

    code = {'type': 'one_hot', 0:'001', 1:'010', 2:'100'}

    print(f'code: {code}')

    code: {'type': 'one_hot', 0: '001', 1: '010', 2: '100'}

In [9]:

    # Exemplul 7: crearea unui dictionar de liste

    irregular_matrix = dict(row0 = [1, 6, 5],  row1 = [3,0,6,2], row2 = [5,5])

    print(f'irregular_matrix: {irregular_matrix}')

    irregular_matrix: {'row0': [1, 6, 5], 'row1': [3, 0, 6, 2], 'row2': [5, 5]}

In [10]:

    # Exemplul 8: crearea unui dictionar de dictionare

    army = { 'warrior_1':{'name':'John',  'weapon': 'sword', 'battle_strength': 8}, \
             'warrior_2':{'name':'Robin', 'weapon': 'bowl',  'battle_strength': 10} }

    print(f'army: {army}')

    army: {'warrior_1': {'name': 'John', 'weapon': 'sword', 'battle_strength': 8}, 'warrior_2': {'name': 'Robin', 'weapon': 'bowl', 'battle_strength': 10}}

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

Sintaxa:

    obiect_dictionar = dict(cheie_1 = valoare_1, ... , cheie_n = valoare_n)
                                            # genereaza un obiect dictionar care contine
                                            # colectia de elemente cheie:valoare
                                            # cheie reprezinta un nume de variabila
                                            # care trebuie sa respecte restrictiile
                                            # specfice denumirilor de variabile
                                            # impuse in limbajul Python

Apelarea metodei `dict()` determina crearea unui obiect de tip `dict`, pe baza continutului furnizat ca argument. Metoda `dict()` se comporta ca un constructor de clasa. Observatie: informatia plasata in campul cheie nu trebuie sa fie un obiect de tip string.

In [11]:

    # Exemplul 9: crearea unui dictionar, apeland la constructorul dict()

    warrior = dict(name = 'Bil', wheapon = 'speer', battle_strength = 9)

    print(f'warrior: {warrior}')

    warrior: {'name': 'Bil', 'wheapon': 'speer', 'battle_strength': 9}

### Functia `zip()`¶

Limbajul de programare Python furnizeaza functia predefinita `zip()`, care combina colectiile furnizate ca argument, returnand un obiect de tip `zip`, compus din K obiecte de tip `tuple`, unde fiecare j obiect `tuple` (j =0, K) contine cel de-al j-lea element din colectiile combinate. In cazul in care lungimile colectiilor combinate sunt diferite, valoarea lui K devine egala cu lungimea cea mai mica a celor 2 colectii.

Sintaxa:

    zip(colectie_1, ... , colectie_n)       # genereaza un obiect zip care contine
                                            # K obiecte tuple, fiecare obiect j avand
                                            # elementul j din fiecare colectie

In [12]:

    # Exemplul 10.a: Afisarea informatiilor despre functia zip

    help(zip)

    Help on class zip in module builtins:

    class zip(object)
     |  zip(*iterables) --> zip object
     |
     |  Return a zip object whose .__next__() method returns a tuple where
     |  the i-th element comes from the i-th iterable argument.  The .__next__()
     |  method continues until the shortest iterable in the argument sequence
     |  is exhausted and then it raises StopIteration.
     |
     |  Methods defined here:
     |
     |  __getattribute__(self, name, /)
     |      Return getattr(self, name).
     |
     |  __iter__(self, /)
     |      Implement iter(self).
     |
     |  __next__(self, /)
     |      Implement next(self).
     |
     |  __reduce__(...)
     |      Return state information for pickling.
     |
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |
     |  __new__(*args, **kwargs) from builtins.type
     |      Create and return a new object.  See help(type) for accurate signature.

In [13]:

    # Exemplul 10.b: crearea unui dictionar, pe baza functiei zip()
    keys = ['0000', '0001', '0010', '0011',
            '0100', '0101', '0110', '0111',
            '1000', '1001', '1010', '1011',
            '1100', '1101', '1110', '1111'
           ]
    values = ['0', '1', '2', '3',
              '4', '5', '6', '7',
              '8', '9', 'A', 'B',
              'C', 'D', 'E', 'F']

    bin2hex = dict(zip(keys,values))

    print(f'bin2hex: {bin2hex}')

    bin2hex: {'0000': '0', '0001': '1', '0010': '2', '0011': '3', '0100': '4', '0101': '5', '0110': '6', '0111': '7', '1000': '8', '1001': '9', '1010': 'A', '1011': 'B', '1100': 'C', '1101': 'D', '1110': 'E', '1111': 'F'}

### 1.3. Utilizarea metodei dict-comprehension¶

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

Sintaxa:

    obiect_dictionar = { expresie  for  item  in  secventa  if  conditie(item) }

sau

    obiect_dictionar = { expresie  for  key, value  in  secventa  if  conditie(key,value) }

unde secventa reprezinta o colectie de elemente, iar expersie reprezinta o functie de item sau de perechea key, value, sau o expresie care poate sau nu sa includa ca operanzi item sau de perechea key, value.

Executie:

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

In [14]:

    # Exemplul 11: crearea unui dictionar conversie cod zecimal - cod binar

    bin_code_list = ['000', '001', '010', '011', '100', '101', '110', '111']

    dec2bin = { k:v for k,v in enumerate(bin_code_list) }    # enumerate returneaza perechi index - valoare

    print(f'dec2bin: {dec2bin}')

    dec2bin: {0: '000', 1: '001', 2: '010', 3: '011', 4: '100', 5: '101', 6: '110', 7: '111'}

In [16]:

    # Exemplul 12: crearea unui dictionar de puteri cu 2 a unui sir de numere pare

    even_squared_numbers = { k:v*v for k,v in enumerate(range(10)) if k%2 == 0}

    print(f'even_squared_numbers: {even_squared_numbers}')

    even_squared_numbers: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

### 1.4 Utilizarea metodei `fromkeys()`¶

Metoda `fromkeys()` furnizata de clasa `dict` genereaza un obiect de tipul dictionar, in care toate cheile sunt initializate cu o valoare comuna.

Sintaxa:

    obiect_dictionar = dict.fromkeys(chei, valoare_comuna)  # chei reprezinta o colectie de chei

In [17]:

    # Exemplul 13: initializarea cheilor unui dictionar cu o valoare comuna

    players = ['Ana', 'Bili', 'Cat']
    score   = 0

    game_table = dict.fromkeys(players, score)

    print(f'game_table: {game_table}')

    game_table: {'Ana': 0, 'Bili': 0, 'Cat': 0}

# 2. Accesarea unui element al unui dictionar¶

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

Un dictionar reprezinta o colectie de elemente, in care fiecare element este identificat si referit pe baza cheii sale.

- un element dintr-un dictionar poate fi accesat prin intermediul operatorului subscript `[]`

Sintaxa:

    dictionar[cheie]    # returneaza elementul cu cheia precizata;

Observatie:

- Daca elementul indentificat prin cheie nu apartine dictionarului, atunci utilizarea operatorului subscript `[]` lanseaza un mesaj de eroare de tipul `KeyError`. Pentru evitarea acestui tip de eroare este necesar ca, inainte de a accesa un element intr-un dictionar, sa se verifice existenta cheii prin care acesta este referit.

In [28]:

    # Exemplul 14: accesarea elementelor unui dictionar pe baza operatorului subscript

    programmer_dict = dict(name = 'Bili', age = 24, skills = ['Python', 'Java', 'C'])

    key = 'name'  #'paid'

    if key in programmer_dict:
        print(f'{key}: {programmer_dict[key]}')
    else:
        print(f'"{key}" key does not exist in dictionary')

    name: Bili

### 2.2. Utilizarea metodei `get()`¶

Metoda `get()` furnizata de catre clasa `dict` furnizeaza o solutie rapida de accesare a unui element dintr-un dictionar, care nu necesita verificarea existentei cheii.

Sintaxa:

    dictionar.get(cheie, valoare_returnata)     # daca in dictionar exista "cheie",
                                              # atunci returneaza elementul identificat prin aceasta
                                              # altfel, returneaza "valoare_returnata"

In [18]:

    # Exemplul 15: accesarea unui element al unui dictionar, cu metoda get()

    programmer_dict = dict(name = 'Bili', age = 24, skills = ['Python', 'Java', 'C'])

    key = 'name'  #'paid'

    programmer_dict.get(key , '{key} does not exist in dictionary' )

Out[18]:

    'Bili'

### 2.3. Utilizarea metodei `setdefault()` (returneaza sau insereaza)¶

Metoda `setdefault()` furnizata de catre clasa `dict` furnizeaza o solutie de accesarea unui element dintr-un dictionar/inserare a unui element nou in dictionar.

Sintaxa:

    dictionar.setdefault(cheie, valoare_initiala)     # daca in dictionar exista "cheie",
                                                    # atunci returneaza elementul identificat prin aceasta
                                                    # altfel, insereaza in dictionar un nou element
                                                    # identificat prin perechea "cheie", "valoare_initiala"

Metoda `setdefault()` furnizata de catre clasa `dict` returneaza valoarea care corespunde cheii. Daca cheie nu exista in dictionar, atunci metoda insereaza in dictionar un nou element definit de perechea cheie - valoare_initiala.

In [19]:

    # Exemplul 16: accesarea unui element al unui dictionar, cu metoda setdefault()

    programmer_dict = dict(name = 'Bili', age = 24, skills = ['Python', 'Java', 'C'])

    key = 'paid' # 'name'

    value = programmer_dict.setdefault(key , 60000 )

    print(value)
    print(programmer_dict)

    60000
    {'name': 'Bili', 'age': 24, 'skills': ['Python', 'Java', 'C'], 'paid': 60000}

# 3. Accesarea tuturor elementelor dintr-un dictionar¶

Metoda `dict` furnizeaza 3 metode care pot fi utilizate pentru accesarea simultana a tuturor elementelor din continutul unui dictionar:

- `items()`
- `keys()`
- `values()`

Metoda `items()` returneaza o colectie care contine toate elementele unui dictionar, in care fiecare element este reprezentat sub forma unui tuple (cheie, valoare).

Sintaxa:

    dictionar.items()     # returneaza o colectie contine elemenetele dictionarului

Metoda `keys()` returneaza o colectie care contine toate cheile elementelor din continutul unui dictionar.

Sintaxa:

    dictionar.keys()      # returneaza o colectie contine cheile elementelor din dictionar

Metoda `values()` returneaza o colectie care contine toate valorile elementelor din continutul unui dictionar.

Sintaxa:

    dictionar.values()     # returneaza o colectie contine valorile elementelor din dictionar

In [20]:

    # Exemplul 17: accesarea elementelor din continutul unui dictionar

    programmer_dict = dict(name = 'Bili', age = 24, skills = ['Python', 'Java', 'C'], paid = 60000)

    keys = programmer_dict.keys()
    values = programmer_dict.values()
    items = programmer_dict.items()

    print('keys: ', keys, end = '\n'*2)
    print('values: ', values, end = '\n'*2)
    print('items: ', items)

    keys:  dict_keys(['name', 'age', 'skills', 'paid'])

    values:  dict_values(['Bili', 24, ['Python', 'Java', 'C'], 60000])

    items:  dict_items([('name', 'Bili'), ('age', 24), ('skills', ['Python', 'Java', 'C']), ('paid', 60000)])

# 4. Setarea valorii elementului unui dictionar¶

Pentru setarea valorii unui element dintr-un dictionar, se utilizeaza operatorul subscript `[]` utilizat cu cheia elementului.

Sintaxa:

    dictionar[cheie] = valoare     # seteaza valoarea elementului referit de cheie la valoare
                                      # daca cheie nu exista in dictionar,
                                      # insereaza in dictionar un element nou cheie-valoare

Observatie: In cazul in care cheie nu apartine dictionarului, la executia instructiunii se insereaza in dictionar un nou element, caracterizat de perechea cheie, valoare.

In [22]:

    # Exemplul 18: setarea unui element al unui dictionar la o noua valoare

    programmer_dict = dict(name = 'Bili', age = 24, skills = ['Python', 'Java', 'C'], paid = 60000)

    programmer_dict['paid'] = 40000                     # modificare valoare cheie

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

    programmer_dict['position'] = 'junior'              # inserare item nou 'position':'junior'

    print(programmer_dict)

    {'name': 'Bili', 'age': 24, 'skills': ['Python', 'Java', 'C'], 'paid': 40000}

    {'name': 'Bili', 'age': 24, 'skills': ['Python', 'Java', 'C'], 'paid': 40000, 'position': 'junior'}

# 5. Actualizarea continutului unui dictionar¶

Continutul unui dictionar se poate actualiza cu valori noi, pentru cheile existente, sau cu elemente noi. In acest scop, clasa `dict` furnizeaza metoda `update()`. Prin apelarea acestei metode, nu se returneaza un nou dictionar; efectul consta in modificarea continutului dictionarului original.

Sintaxa:

    dictionar.update(continut_nou)  # actualizeaza continutul dictionarului original cu continut_nou
                                       # nu returneaza nicio valoare (returneaza valoarea None).

Observatie:

- continut_nou poate fi reprezentat de o colectie sau perechi cheie-valoare, de obicei furnizate sub forma unor tuple

In [23]:

    # Exemplul 19: actualizarea valorilor elementelor unui dictionar

    programmer_dict = dict(name = 'Bili', age = 24, skills = ['Python', 'Java'], paid = 40000)
    position_dict   = dict(position = 'junior', department = 'R&D')

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

    programmer_dict.update(position_dict)

    print(programmer_dict)

    {'name': 'Bili', 'age': 24, 'skills': ['Python', 'Java'], 'paid': 40000}

    {'name': 'Bili', 'age': 24, 'skills': ['Python', 'Java'], 'paid': 40000, 'position': 'junior', 'department': 'R&D'}

In [24]:

    # Exemplul 20: actualizarea continutului unui dictionar cu elemente noi

    dec2letters = dict(zip(range(3),'abc'))
    print('before', dec2letters, end = '\n'*2)

    dec2letters.update(d = 3, e = 4)

    print('after ', dec2letters)

    before {0: 'a', 1: 'b', 2: 'c'}

    after  {0: 'a', 1: 'b', 2: 'c', 'd': 3, 'e': 4}

# 6. Stergerea unui element dintr-un dictionar¶

Stergerea unui element dintr-un dictionar elimina permanent elementul respectiv din continutul dictionarului.

### Stergerea ultimului element inserat / a unui element aleator dintr-un dictionar¶

Este realizata cu metoda `popitem()` furnizata de clasa `dict`. Aceasta metoda sterge din dictionar ultimul element inserat, sau, pentru versiuni anterioare versiunii Python 3.7, sterge un element aleator. Metoda nu returneaza un nou obiect dictionar ci modifica continutul dictionarului original.

Sintaxa:

    dict.popitem()        # sterge un element aleator din dictionar
                             # nu returneaza (returneaza valoarea None)

Observatie:

- Daca dictionarul este gol, la executia acestei metode se lanseaza un mesaj de tipul KeyError. Pentru evitarea acestei erori, se recomanda ca inainte de a fi sters un element din dictionar, sa se verifice daca acesta este gol.

In [25]:

    # Exemplul 21: stergerea ultimului element inserat intr-un dictionar

    dec2letters = dict(zip(range(3),'abc'))

    while dec2letters != {}:               # atat timp cat dictionarul nu este gol
                                           # la fiecare iteratie
        print(dec2letters, end = '\n'*2)   # afiseaza continutul dictionarului si
        dec2letters.popitem()              # sterge ultimul item din dictionar
    else:
        print('END')

    {0: 'a', 1: 'b', 2: 'c'}

    {0: 'a', 1: 'b'}

    {0: 'a'}

    END

### Stergerea din dictionar a unui element precizat prin cheie¶

Este realizata cu metoda `pop()` furnizata de clasa `dict`. Utilizarea acestei metode elimina necesitatea verificarii existentei cheii in dictionar.

Sintaxa:

    dict.pop(cheie, valoare_returnata)        # sterge elementul identificat prin "cheie"
                                                 # returneaza valoarea elementului sters
                                                 # daca "cheie" nu exista in dictionar, returneaza "valoare_returnata"

In [26]:

    # Exemplul 22: stergerea unui element dintr-un dictionar, identificat prin cheie

    odd_dict = {i:i**2 for i in range(10) if i%2}  # dictionar - contine puterile lui 2 ale nr. impare [0,10)
    print(odd_dict, end = '\n'*2)

    for i in range(10):
        val = odd_dict.pop(i, 'x')     # daca i este cheie in odd_dict returneaza i**2, altfel 'x'
        print(val, end = ' ')

    {1: 1, 3: 9, 5: 25, 7: 49, 9: 81}

    x 1 x 9 x 25 x 49 x 81

### Stergerea din dictionar a unui element apeland functia Python `del()`¶

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

Sintaxa:

    del(dictionar[cheie])        # sterge elementul precizat prin cheie, din dictionar

Observatie:

- In cazul in care cheie nu exista in dictionar, la executia functiei se lanseaza un mesaj de eroare de tipul `KeyError`. Pentru evitarea erorii, se recomanda ca, inainte de a sterge un element dintr-un dictionar, sa se verifice apartenenta cheii sale la dictionarul respectiv.

# 7. Stergerea continutului unui dictionar¶

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

Sintaxa:

    dict.clear()            # sterge intregul continut al dictionarului;
                               # nu returneaza (returneaza valoarea None)

In [27]:

    # Exemplul 23: stergerea continutului unui dictionar

    even_dict = {i:i**2 for i in range(10) if not i%2}  # dictionar - contine puterile lui 2 ale nr. impare [0,10)
    print(even_dict, end = '\n'*2)

    even_dict.clear()
    print(even_dict)

    {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

    {}

# 8. Copierea listelor¶

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

- shallow copy = copia dictionarului reprezinta un alias al dictionarului original, ambele dictionare 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 dictionarului reprezinta un obiect diferit fata de dictionarul 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 `dict`.

Sintaxa "shallow copy":

    dictionar_copie = dictionar_original               # dictionar_copie este acelasi obiect ca si dictionar_original

Sintaxa "deep copy":

    dictionar_copie = dictionar_original.copy()        # dictionar_copie reprezinta un obiecct diferit
                                                          # fata de dictionar_original
                                                          # dar cu acelasi continut

In [28]:

    # Exemplul 24: rezultatul obtinut prin tehnica "shallow copy"

    person = {'name': 'Bili', 'age': 25}
    print('person - before:', person, end = '\n'*2)

    another_person = person         # shallow copy

    another_person['name'] = "Gimi"

    print('person - after:', person, end = '\n'*2)
    print('another_person:', another_person)

    person is another_person       # ambele variabile reprezinta referinte catre acelasi obiect

    person - before: {'name': 'Bili', 'age': 25}

    person - after: {'name': 'Gimi', 'age': 25}

    another_person: {'name': 'Gimi', 'age': 25}

Out[28]:

    True

In [29]:

    # Exemplul 25: rezultatul obtinut prin tehnica "deep copy"

    person = {'name': 'Bili', 'age': 25}
    print('person - before:', person, end = '\n'*2)

    another_person = person.copy()         # deep copy

    another_person['name'] = "Gimi"

    print('person - after:', person, end = '\n'*2)
    print('another_person:', another_person)

    person is another_person       # variabilele reprezinta referinte catre obiecte diferite

    person - before: {'name': 'Bili', 'age': 25}

    person - after: {'name': 'Bili', 'age': 25}

    another_person: {'name': 'Gimi', 'age': 25}

Out[29]:

    False

# 9. Iterarea elementelor unui dictionar¶

Este realizata pe baza buclei `for`.

In [32]:

    # Exemplul 26: iterarea cheilor unui dictionar

    a_dict = dict(zip('abcdefgh', range(8)))
    print(a_dict, end = 2*'\n')

    print('KEYS:')
    for k in a_dict.keys():
        print(f'key: {k}')

    {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7}

    key: a
    key: b
    key: c
    key: d
    key: e
    key: f
    key: g
    key: h

In [33]:

    # Exemplul 27: iterarea valorilor unui dictionar

    a_dict = dict(zip('abcdefgh', range(8)))
    print(a_dict, end = 2*'\n')

    print('VALUES: ')
    for v in a_dict.values():
        print(f'value: {v}')

    {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7}

    VALUES:
    value: 0
    value: 1
    value: 2
    value: 3
    value: 4
    value: 5
    value: 6
    value: 7

In [35]:

    # Exemplul 28: iterarea elementelor unui dictionar

    a_dict = dict(zip('abcdefgh', range(8)))
    print(a_dict, end = 2*'\n')

    print('ITEMS: ')
    for k,v in a_dict.items():
        print(f'{k} : {v}')

    {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7}

    ITEMS:
    a : 0
    b : 1
    c : 2
    d : 3
    e : 4
    f : 5
    g : 6
    h : 7

# 10. Testarea egalitatii dictionarelor¶

Se realizeaza pe baza operatorilor relationali `==` si `!=`. Operatorii relationali conecteaza dictionarele testate in expresii booleene, care sunt evaluate `True` sau `False`, in functie de continutul acestora. Continutul a doua dictionare este considerat egal, daca acestea au elemente identice (cheile si valorile elementelor comparate sunt egale); ordinea elementelor in dictionar nu conteaza, deoarece dictionarele sunt colectii neordonate.

In [36]:

    # Exemplul 29: testarea egalitatii a 2 dictionare

    a_dict = {'name': 'Gimi', 'age': 8}
    b_dict = {'name': 'Gimi', 'age': 8}

    eq = a_dict == b_dict
    n_eq = a_dict != b_dict

    print(f'Equality_test:     {eq}')
    print(f'Non-equality_test: {n_eq}')

    Equality_test:     True
    Non-equality_test: False

# 11. Testarea identitatii dictionarelor¶

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

In [37]:

    # Exemplul 30: testarea identitatii a 2 dictionare
    a_dict = {'name': 'Gimi', 'age': 8}
    b_dict = {'name': 'Gimi', 'age': 8}

    same     = a_dict is b_dict
    not_same = a_dict is not b_dict

    print(f'Identity:          {same}')
    print(f'Non-identity_test: {not_same}')

    Identity:          False
    Non-identity_test: True