In limbajul de programare Python, seturile (multimile) reprezinta obiecte din clasa `set`, care au urmatoarele proprietati:

- reprezinta containere utilizate pentru stocarea unor colectii neordonate de obiecte; intr-o colectie, fiecare obiect se numeste element sau item.
- prin colectie neordonata se intelege o colectie de elemente in care acestea nu pot fi identificate si referite (utilizate) prin intermediul unui index (numar intreg unic);
- elementele sunt unice (nu pot fi duplicate);
- obiectul de tip `set` este mutabil - dupa crearea setului, pot fi adaugate elemente in set, sau pot fi sterse, etc.
- seturile sunt utile de obicei in operatii de testare a apartenentei la o colectie de obiecte, in operatii de eliminare a elementelor duplicate dintr-o colectie de obiecte, in operatii logice cu continuturile unor colectii.

In [1]:

    # Exemplul 1: listarea metodelor clasei Set
    help(set)

    Help on class set in module builtins:

    class set(object)
     |  set() -> new empty set object
     |  set(iterable) -> new set object
     |
     |  Build an unordered collection of unique elements.
     |
     |  Methods defined here:
     |
     |  __and__(self, value, /)
     |      Return self&value.
     |
     |  __contains__(...)
     |      x.__contains__(y) <==> y in x.
     |
     |  __eq__(self, value, /)
     |      Return self==value.
     |
     |  __ge__(self, value, /)
     |      Return self>=value.
     |
     |  __getattribute__(self, name, /)
     |      Return getattr(self, name).
     |
     |  __gt__(self, value, /)
     |      Return self>value.
     |
     |  __iand__(self, value, /)
     |      Return self&=value.
     |
     |  __init__(self, /, *args, **kwargs)
     |      Initialize self.  See help(type(self)) for accurate signature.
     |
     |  __ior__(self, value, /)
     |      Return self|=value.
     |
     |  __isub__(self, value, /)
     |      Return self-=value.
     |
     |  __iter__(self, /)
     |      Implement iter(self).
     |
     |  __ixor__(self, value, /)
     |      Return self^=value.
     |
     |  __le__(self, value, /)
     |      Return self<=value.
     |
     |  __len__(self, /)
     |      Return len(self).
     |
     |  __lt__(self, value, /)
     |      Return self<value.
     |
     |  __ne__(self, value, /)
     |      Return self!=value.
     |
     |  __or__(self, value, /)
     |      Return self|value.
     |
     |  __rand__(self, value, /)
     |      Return value&self.
     |
     |  __reduce__(...)
     |      Return state information for pickling.
     |
     |  __repr__(self, /)
     |      Return repr(self).
     |
     |  __ror__(self, value, /)
     |      Return value|self.
     |
     |  __rsub__(self, value, /)
     |      Return value-self.
     |
     |  __rxor__(self, value, /)
     |      Return value^self.
     |
     |  __sizeof__(...)
     |      S.__sizeof__() -> size of S in memory, in bytes
     |
     |  __sub__(self, value, /)
     |      Return self-value.
     |
     |  __xor__(self, value, /)
     |      Return self^value.
     |
     |  add(...)
     |      Add an element to a set.
     |
     |      This has no effect if the element is already present.
     |
     |  clear(...)
     |      Remove all elements from this set.
     |
     |  copy(...)
     |      Return a shallow copy of a set.
     |
     |  difference(...)
     |      Return the difference of two or more sets as a new set.
     |
     |      (i.e. all elements that are in this set but not the others.)
     |
     |  difference_update(...)
     |      Remove all elements of another set from this set.
     |
     |  discard(...)
     |      Remove an element from a set if it is a member.
     |
     |      If the element is not a member, do nothing.
     |
     |  intersection(...)
     |      Return the intersection of two sets as a new set.
     |
     |      (i.e. all elements that are in both sets.)
     |
     |  intersection_update(...)
     |      Update a set with the intersection of itself and another.
     |
     |  isdisjoint(...)
     |      Return True if two sets have a null intersection.
     |
     |  issubset(...)
     |      Report whether another set contains this set.
     |
     |  issuperset(...)
     |      Report whether this set contains another set.
     |
     |  pop(...)
     |      Remove and return an arbitrary set element.
     |      Raises KeyError if the set is empty.
     |
     |  remove(...)
     |      Remove an element from a set; it must be a member.
     |
     |      If the element is not a member, raise a KeyError.
     |
     |  symmetric_difference(...)
     |      Return the symmetric difference of two sets as a new set.
     |
     |      (i.e. all elements that are in exactly one of the sets.)
     |
     |  symmetric_difference_update(...)
     |      Update a set with the symmetric difference of itself and another.
     |
     |  union(...)
     |      Return the union of sets as a new set.
     |
     |      (i.e. all elements that are in either set.)
     |
     |  update(...)
     |      Update a set with the union of itself and others.
     |
     |  ----------------------------------------------------------------------
     |  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 seturi:

| Operatie | Python
--- | --- | ---
01. | crearea unui obiect de tip set: | `{}`
|  | `set()`
02. | adaugarea unui item nou: | `ob_set.add(item)`
03. | extinderea unui set: | `ob_set.update(colectie)`
04. | stergerea unui item: | `ob_set.remove(item)`
|  | `ob_set.discard(item)`
|  | `ob_set.pop()`
05. | stergerea tuturor item-urilor: | `ob_set.clear()`
06. | copierea seturilor: | `ob_set.copy()`
07. | iterare item-uri: | `for`
08. | operatii matematice: | `ob_set.union(alt_ob_set)`
|  | `ob_set.intersection(alt_ob_set)`
|  | `ob_set.difference(alt_ob_set)`
09. | teste egalitate: | `==` `!=`
10. | teste identitate: | `is` `is not`

ob_set = obiect de tipul set

# 1. Crearea unui obiect de tip set¶

### 1.1 Metoda directa¶

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

Sintaxa:

    obiect_set = {item_1, item_2, ...., item_N}   # elementele sunt obiecte de orice tip

In [2]:

    # Exemplul 1: crearea unui set de numere intregi

    int_set = {1 ,4, 2, 4}  # valoarea 4 este duplicata in mod intentionat;
                            # un obiect set nu permite duplicarea elementelor

    print(f'int_set : {int_set}', end = '\n'*2)

    # Exemplul 2: crearea unui set de obiecte mixte

    mix_set = {1 , '4', 2.5}

    print(f'mix_set : {mix_set}')

    int_set : {1, 2, 4}

    mix_set : {1, 2.5, '4'}

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

Sintaxa:

    obiect_set = set(colectie_elemente)   # genereaza un obiect set care contine colectia de elemente

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

In [3]:

    # Exemplul 3: crearea unui set gol

    empty_set = set() # {} nu creeaza un obiect set gol!

    print(f'empty_set : {empty_set}', end = '\n'*2)

    # Exemplul 4: crearea unui set de string-uri

    str_set = set('Hellooooo!')

    print(f'str_set   : {str_set}')

    empty_set : set()

    str_set   : {'e', 'o', 'H', '!', 'l'}

### 1.3. Utilizarea metodei set-comprehension¶

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

Sintaxa:

    obiect_set = [ 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 setului rezultatul generat de expresie, apoi se trece la urmatoare iteratie.
    - daca conditie(item) nu este validata, atunci se trece la urmatoare iteratie.

In [4]:

    # Exemplul 5: crearea unui set utilizand metoda set comprehension

    smart_set = {item.upper() for item in 'fGG3fsFfSrf'}

    print(f'smart_set   : {smart_set}')

    smart_set   : {'F', '3', 'S', 'R', 'G'}

# 4. Introducerea unui element nou intr-un obiect de tip set¶

Pentru adaugarea unui nou element intr-un set, clasa `set` furnizeaza metoda `add()`.

Sintaxa:

    obiect_set.add(item_nou)         # adauga in set un item_nou,
                                        # nu returneaza un obiect set nou (returneaza valoarea None).

In [5]:

    # Exemplul 6: adaugarea unui element nou intr-un set

    num_set = {1,2,4}
    print(f'num_set   : {num_set}', end = '\n'*2)

    num_set.add(3)

    print(f'num_set   : {num_set}')

    num_set   : {1, 2, 4}

    num_set   : {1, 2, 3, 4}

# 5. Actualizarea continutului unui obiect de tip set¶

Pentru actualizarea (extinderea) continutului unui obiect de tip `set`, clasa `set` furnizeaza metoda `update()`.

Sintaxa:

    obiect_set.update(colectie)         # extinde continutul obiectului set cu o colectie de elemente,
                                           # nu returneaza un obiect set nou (returneaza valoarea None).

In [6]:

    # Exemplul 7: adaugarea unui element nou intr-un set

    num_set = {1,2,3}
    print(f'num_set   : {num_set}', end = '\n'*2)

    num_set.update([2,3,4,5])
    print(f'num_set   : {num_set}')

    num_set   : {1, 2, 3}

    num_set   : {1, 2, 3, 4, 5}

# 6. Stergerea elementelor din continutul unui obiect de tip set¶

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

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

Sintaxa:

    obiect_set.remove(item)        # sterge din set elementul identificat prin argumentul item
                                      # continutul original al setului se modifica,
                                      # lungimea acestuia descrescand cu 1,
                                      # nu returneaza (returneaza valoarea None)

Observatie:
In cazul in care elementul furnizat ca argument metodei `remove()` nu exista in continutul setului, la executia metodei se lanseaza un mesaj de eroare de tipul `KeyError`. Pentru evitarea acestei erori, se recomanda ca inainte de a sterge un element dintr-un set sa se verifice apartenenta sa la setul respectiv, prin utilizarea operatorului de apartenenta `in`.

In [7]:

    # Exemplul 8: Stergerea unui element dintr-un set cu metoda remove()

    chr_set = set('skfksdjnffnqk')
    print(f'chr_set   : {chr_set}', end = '\n'*2)

    item = 'f'

    if item in chr_set:
        chr_set.remove(item)

    print(f'chr_set   : {chr_set}')

    chr_set   : {'q', 'd', 's', 'j', 'n', 'k', 'f'}

    chr_set   : {'q', 'd', 's', 'j', 'n', 'k'}

Clasa `set` furnizeaza in acelasi scop (stergerea unui element) si metoda `discard()`. Diferenta dintre metoda `remove()` si metoda `discard()` consta in faptul ca, la executia celei din urma, daca elementul furnizat ca argument nu apartine continutul setului respectiv, metoda nu lanseaza un mesaj de eroare.

Sintaxa:

    obiect_set.discard(item)       # sterge din set elementul identificat prin argumentul item
                                      # continutul original al setului se modifica,
                                      # lungimea acestuia descrescand cu 1,
                                      # nu returneaza (returneaza valoarea None)

In [8]:

    # Exemplul 9: Stergerea unui element dintr-un set cu metoda discard()

    chr_set = set('skksdjnffnqk')
    print(f'chr_set   : {chr_set}', end = '\n'*2)

    item = 'q' # 'x'

    chr_set.discard(item)

    print(f'chr_set   : {chr_set}')

    chr_set   : {'q', 'd', 's', 'j', 'n', 'k', 'f'}

    chr_set   : {'d', 's', 'j', 'n', 'k', 'f'}

In cazul in care, la stergere a unui element, este necesar ca acesta sa fie utilizat in cod, clasa `set` furnizeaza metoda `pop()`. La executia metodei, aceasta sterge un element arbitrar, pe care il si returneaza. In cazul in care continutul obiectului set este gol, la executia metodei se lanseaza un mesaj de eroare de tipul `KeyError`. Pentru evitarea acestei erori, se recomanda ca inainte de a sterge un element dintr-un set sa se verifice daca setul este gol; o solutie consta in utilizarea functiei `len()` care returneaza numarul de elemente din continutul setului.

Sintaxa:

    obiect_set.pop()       # sterge din set un element arbitrar
                              # continutul original al setului se modifica,
                              # lungimea acestuia descrescand cu 1,
                              # returneaza elementul sters

In [9]:

    # Exemplul 10: Stergerea unui element arbitrar dintr-un set cu metoda pop()

    chr_set = set('skksdjnffnqk')
    print(f'chr_set   : {chr_set}', end = '\n'*2)

    if len(chr_set):                       # len(a_set) este evaluat ca True numai daca obicetul set nu este gol
        removed_item = chr_set.pop()

    print(f'chr_set   : {chr_set}', end = '\n'*2)
    print(removed_item)

    chr_set   : {'q', 'd', 's', 'j', 'n', 'k', 'f'}

    chr_set   : {'d', 's', 'j', 'n', 'k', 'f'}

    q

# 7. Stergerea intregului continut al unui obiect de tip set¶

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

Sintaxa:

    obiect_set.clear()           # sterge intregul continut al setului;
                                    # nu returneaza (returneaza valoarea None)

In [10]:

    # Exemplul 11: stergerea continutului unui set

    chr_set = set('skksdjnffnqk')
    print(f'chr_set   : {chr_set}', end = '\n'*2)

    if len(chr_set):                       # len(a_set) este evaluat ca True numai daca obicetul set nu este gol
        chr_set.clear()

    print(f'chr_set   : {chr_set}')

    chr_set   : {'q', 'd', 's', 'j', 'n', 'k', 'f'}

    chr_set   : set()

# 8. Copierea obiectelor de tip set¶

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

- shallow copy = copia obiectului set reprezinta un alias al obiectului set original, ambele variabile reprezentand acelasi obiect set; in acest caz, oricare modificare a continutului copiei, modifica si continutul originalulului; aceasta tehnica este implementata pe baza operatorului de atribuire `=`.
- deep copy = copia obiectului set reprezinta un obiect diferit fata de obiectul set 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 `set`.

Sintaxa "shallow copy":

    set_copie = set_original                    # set_copie este acelasi obiect cu set_original

Sintaxa "deep copy":

    set_copie = set_originala.copy()            # set_copie reprezinta un obiect diferit fata de set_original
                                                   # dar cu acelasi continut

In [11]:

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

    a_set = {1,2}
    b_set = a_set                                           # shallow copy

    print(f'a_set: {a_set} \nb_set: {b_set}')
    print(f'\n"a_set" same object as "b_set": {a_set is b_set}','\n')  # ambele variabile reprezinta acelasi obiect

    b_set.add('x')                                       # modificarile asupra variabilei b_set sunt operate implicit si in a_set
    print(f'a_set: {a_set}, \nb_set: {b_set}')

    a_set: {1, 2}
    b_set: {1, 2}

    "a_set" same object as "b_set": True

    a_set: {1, 2, 'x'},
    b_set: {1, 2, 'x'}

In [12]:

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

    a_set = {1,2}
    b_set = a_set.copy()                                    # deep copy

    print(f'a_set: {a_set}, \nb_set: {b_set}')
    print(f'\n"a_set same" object as "b_set": {a_set is b_set}','\n')  # variabilele reprezinta obiecte diferite

    b_set.add('x')                                          # modificarile asupra variabilei b_set nu sunt operate si in a_set
    print(f'a_set: {a_set}, \nb_set: {b_set}')

    a_set: {1, 2},
    b_set: {1, 2}

    "a_set same" object as "b_set": False

    a_set: {1, 2},
    b_set: {1, 2, 'x'}

# 9. Iterarea elementelor unui obiect de tip set¶

Este realizata pe baza buclei `for`.

In [15]:

    # Exemplul 14: iterarea elementelor unui obiect set

    num_set = {item for item in range(128557, 128592)}

    for item in num_set:
        print(chr(item))

    😭
    😮
    😯
    😰
    😱
    😲
    😳
    😴
    😵
    😶
    😷
    😸
    😹
    😺
    😻
    😼
    😽
    😾
    😿
    🙀
    🙁
    🙂
    🙃
    🙄
    🙅
    🙆
    🙇
    🙈
    🙉
    🙊
    🙋
    🙌
    🙍
    🙎
    🙏

# 10. Operatii matematice cu obiecte de tip set¶

Clasa `set` furnizeaza diferite metode similare operatiilor definite in teoria multimilor, care se pot aplica asupra continutului obiectelor de tip set:

- reuniunea continuturilor a 2 obiecte de tip set
- intersectia continuturilor a 2 obiecte de tip set
- diferenta dintre continuturile a 2 obiecte de tip set

Sintaxa:

    set_A.union(set_B)              # returneaza un obiect set care contine toate elementele
                                       # din continuturile obiectelor set_A si set_B
                                       # eliminand duplicatii

Sintaxa:

    set_A.intersection(set_B)       # returneaza un obiect set care contine toate elementele
                                       # comune continuturilor obiectelor set_A si set_B
                                       # eliminand duplicatii

Sintaxa:

    set_A.difference(set_B)         # returneaza un obiect set care contine toate elementele
                                       # din continutul obiectului set_A
                                       # care nu sunt prezente in continutul obiectului set_B

In [17]:

    # Exemplul 15: afisarea infromatiilor despre metoda union

    help(set.union)

    Help on method_descriptor:

    union(...)
        Return the union of sets as a new set.

        (i.e. all elements that are in either set.)

In [18]:

    # Exemplul 16: afisarea infromatiilor despre metoda intersection

    help(set.intersection)

    Help on method_descriptor:

    intersection(...)
        Return the intersection of two sets as a new set.

        (i.e. all elements that are in both sets.)

In [19]:

    # Exemplul 17: afisarea infromatiilor despre metoda difference

    help(set.difference)

    Help on method_descriptor:

    difference(...)
        Return the difference of two or more sets as a new set.

        (i.e. all elements that are in this set but not the others.)

In [20]:

    # Exemplul 18: functii matematice cu seturi

    set_A = set('abddef')
    set_B = set('fehsd')
    print(f'set_A = {set_A} \nset_B = {set_B}', end = '\n'*2)

    union_AB = set_A.union(set_B)
    intersection_AB = set_A.intersection(set_B)
    difference_AB = set_A.difference(set_B)
    difference_BA = set_B.difference(set_A)

    print(f'union_AB:         {union_AB}', end = '\n'*2)
    print(f'intersection_AB:  {intersection_AB}', end = '\n'*2)
    print(f'difference_AB:    {difference_AB}', end = '\n'*2)
    print(f'difference_BA:    {difference_BA}')

    set_A = {'e', 'd', 'a', 'b', 'f'}
    set_B = {'e', 'd', 's', 'h', 'f'}

    union_AB:         {'e', 'd', 'a', 'b', 's', 'h', 'f'}

    intersection_AB:  {'d', 'f', 'e'}

    difference_AB:    {'a', 'b'}

    difference_BA:    {'s', 'h'}

# 11. Testarea egalitatii obiectelor de tip set¶

Se realizeaza pe baza operatorilor relationali `==` si `!=`. Operatorii relationali conecteaza obiectele de tip set testate in expresii booleene, care sunt evaluate `True` sau `False`, in functie de continutul acestora. Continuturile a doua obiecte de tip set sunt considerate egale, daca cele doua obiecte de tip set au elemente identice.

In [24]:

    # Exemplul 16: testarea egalitatii a 2 obiecte de tip set

    a_set = {1,2}
    b_set = {1,2}

    equality_test = a_set == b_set

    print(f'Are a_set and b_set equals? -> {equality_test}')

    Are a_set and b_set equals? -> True

# 12. Testarea identitatii obiectelor de tip set¶

Se realizeaza pe baza operatorilor de identitate `is` si `is not`. Operatorii de identitate conecteaza obiectele de tip set testate in expresii booleene, care sunt evaluate `True` respectiv `False` in functie de relatia in care se afla acestea: acelasi obiect vs. obiecte diferite.

In [25]:

    # Exemplul 17: testarea identitatii a 2 obiecte de tip set
    a_set = {1,2}
    b_set = {1,2}

    identity_test = a_set is b_set

    print(f'Are a_set and b_set identical? -> {identity_test}')

    Are a_set and b_set identical? -> False

In [26]:

    # TEMA_1: eliminarea elementelor duplicate dintr-o colectie de obiecte (caz: lista)

    cars_list = ['Subaru', 'Toyota', 'Honda', 'Honda', 'Toyota', 'Mazda', 'Suzuki', 'Suzuki', 'Honda']
    print(f'cars_list: {cars_list}', '\n\n')

    cars_list = list(set(cars_list))
    print(f'cars_list: {cars_list}', '\n\n')

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

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

In [29]:

    # TEMA 2: test de apartenenta: semnalizarea existentei unui mesaj intr-un text

    keys = {'isis', 'terrorism', 'terrorist', 'bomb', 'kill', 'attaks'}

    text = """
    The Intergovernmental Panel on Climate Change* released a report last year expressing serious concerns
    about the possible impacts of climate change, both in the near and distant future. Broadly speaking,
    people around the world agree that climate change poses a severe risk to their countries, according
    to a 26-nation survey conducted in the spring / //..... of 2018. In 13 of these countries, people name climate
    change as the top international threat. But global warming is just one of many concerns. Terrorism,
    specifically from the Islamic extremist group known as ISIS, and cyberattacks are also seen by many
    as major security threats. In eight of the countries surveyed, including Russia, France, Indonesia and
    Nigeria, ISIS is seen as the top threat. In four nations, including Japan and the United States, people
    see cyberattacks from other countries as their top international concern. One country, Poland, names Russia’s
    power and influence as its top threat, but few elsewhere say Russia is a major concern.
    """

    intab    = "\n.,:/*"
    outtab   = " "*len(intab)
    transtab = str.maketrans(intab, outtab)
    text = text.translate(transtab).lower()
    text_set = set(text.split(' '))

    if text_set.intersection(keys):
        print(f'WARNING! - We found potential threatening messages: {text_set.intersection(keys)}')
    else:
        print('clean text')

    WARNING! - We found potential threatening messages: {'terrorism', 'isis'}