Python - wprowadzenie

10 - Podstawowe kolekcje cz. II - słowniki i zbiory

Słowniki

Listy i krotki, które poznaliśmy w poprzedniej lekcji, posiadają przypisane liczby (indeksy) do każdego elementu. Słowniki zamiast indeksów mają klucze, do których przypisane są wartości. Klucze są unikatowe (podobnie zresztą jak indeksy w listach), czyli nie może występować więcej niż jeden taki sam klucz w danym słowniku. Kluczem może być łańcuch znaków, ale także liczba i inne typy niemodyfikowalne. Wartości mogą się powtarzać. W przeciwieństwie do list kolejność elementów w słownikach nie jest uporządkowana, tzn. jeśli na przykład będziemy iterować klucze w słowniku, to niekoniecznie uzyskamy je w takiej kolejności, w jakiej były w nim umieszczane.

Tworzenie słowników

Utwórzmy pierwszy słownik, w którym przyporządkujemy do siebie symbole nukleotydów do ich znaczenia:

nukleotydy = {'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina',} 
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Dla większej przejrzystości kodu można poszczególne pary klucz-wartość umieścić w osobnych liniach:

nukleotydy = {
    'A': 'Adenina', 
    'C': 'Cytozyna', 
    'G': 'Guanina', 
    'T': 'Tymina',
    } 
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Jak widać, słownik tworzymy podobnie jak listę albo krotkę, jednak słownik jest ograniczony parą nawiasów klamrowych {} a zamiast pojedynczych wartości mamy pary klucz: wartość, przedzielone przecinkami. Zwróć uwagę na przecinek po ostatniej parze, nie jest on obowiązkowy, ale jeśli będziemy chcieli w kodzie dodać kolejne wartości, to nie trzeba będzie pamiętać o rozpoczęciu wpisywania od umieszczenia przecinka.

Innym sposobem tworzenia słowników jest użycie funkcji dict(). W tym przypadku musimy pilnować, żeby klucze były nazywane zgodnie z zasadami określonymi dla zmiennych (nie mogą np. zawierać spacji):

nukleotydy = dict(
    A = 'Adenina', 
    C = 'Cytozyna', 
    G = 'Guanina', 
    T = 'Tymina',
    )
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Pusty słownik możemy uzyskać, używając pary nawiasów klamrowych lub funkcji dict().

slownik_1 = {}
print(slownik_1)
slownik_2 = dict()
print(slownik_2)
{}
{}

Liczbę elementów w słowniku, można sprawdzić za pomocą funkcji len():

print(len(nukleotydy))
4

Uzyskiwanie elementów słownika

Pierwszy ze sposobów uzyskiwania wartości ze słownika przypomina otrzymywanie wartości z listy z użyciem indeksu, tyle że w przypadku słownika podajemy nie indeks (którego nie ma), tylko klucz:

print(nukleotydy['G'])
Guanina

Jeśli podamy klucz, którego nie ma, to uzyskamy komunikat o błędzie:

print(nukleotydy['X'])
---------------------------------------------------------
KeyError                Traceback (most recent call last)

<ipython-input-33-2da619e44e5c> in <module>
----> 1 print(nukleotydy['X'])

KeyError: 'X'

Można tego uniknąć, sprawdzając najpierw, czy klucz znajduje się w słowniku i podjąć próbę uzyskania wartości w przypadku jego obecności:

nukleotyd = input("Podaj symbol:")
if nukleotyd in nukleotydy:
    print(f"Symbol '{nukleotyd}' oznacza: {nukleotydy[nukleotyd]}")
else:
    print(f"Nie mam informacji o symbolu '{nukleotyd}'")
Podaj symbol: X

Nie mam informacji o symbolu 'X'

Drugim sposobem pobrania wartości dla klucza jest użycie metody get():

print(nukleotydy.get('A'))
Adenina

Sprawdźmy, co się stanie, jeśli klucza nie będzie w słowniku:

print(nukleotydy.get('X'))
None

Jak widać, w tym przypadku nie pojawia się błąd, uzyskujemy wartość None. Warto wspomnieć, że nie jest to obiekt typu String, ale obiekt typu NoneType, do którego jeszcze wrócimy przy okazji omawiania funkcji i metod.

type(nukleotydy.get('X'))
NoneType

Możemy nieco zmodyfikować powyższy kod, aby dostosować go do metody get():

nukleotyd = input("Podaj symbol:")
if nukleotydy.get(nukleotyd) != None:
    print(f"Symbol '{nukleotyd}' oznacza: {nukleotydy.get(nukleotyd)}")
else:
    print(f"Nie mam informacji o symbolu '{nukleotyd}'")
Podaj symbol: G

Symbol 'G' oznacza: Guanina

Metoda get() posiada też możliwość ustawienia elementu, który zostanie zwrócony, jeśli podany klucz nie znajduje się w słowniku:

print(nukleotydy.get('X', "Nie ma X-a"))
Nie ma X-a

Warto zauważyć, ze zwracana wartość nie musi być łańcuchem znaków, może to być obiekt innego typu, np. liczba całkowita:

wynik = nukleotydy.get('X', 1)
print(wynik)
print(type(wynik))
1
<class 'int'>

Wszystkie klucze słownika można otrzymać, używając metody keys():

print(nukleotydy.keys())
dict_keys(['A', 'C', 'G', 'T'])

Podobnie, wartości uzyskamy stosując metodę values():

print(nukleotydy.values())
dict_values(['Adenina', 'Cytozyna', 'Guanina', 'Tymina'])

Do pobrania wszystkich par klucz-wartość, przyda nam się metoda items():

print(nukleotydy.items())
dict_items([('A', 'Adenina'), ('C', 'Cytozyna'), ('G', 'Guanina'), ('T', 'Tymina')])

W tym ostatnim przypadku otrzymaliśmy krotki z parami klucz-wartość.

Powyższe metody możemy użyć do iteracji przez klucze, wartości czy pary klucz-wartość, o czym za chwilę.

Dodawanie, modyfikacja i usuwanie elementów słownika

Nowe pary klucz-wartość, można dodać w ten sposób:

nukleotydy['U'] = 'Uran'
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina', 'U': 'Uran'}

Należy przy tym uważać, ponieważ jeśli podamy klucz, który już istnieje, to przypisana mu wartość ulegnie zmianie. Jest to oczywiście sposób modyfikacji par klucz-wartość:

nukleotydy['U'] = 'Uracyl'
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina', 'U': 'Uracyl'}

W celu usunięcia par klucz-wartość można użyć polecenia del:

del nukleotydy['U']
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Innym sposobem jest zastosowanie metody pop(), która jednocześnie zwraca wartość wg. klucza i usuwa parę klucz-wartość:

print(nukleotydy.pop('A'))
print(nukleotydy)
Adenina
{'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Zawartość całego słownika można usunąć za pomocą metody clear():

nukleotydy.clear()
print(nukleotydy)
{}

Słowniki i iteracja

Powyżej poznaliśmy metody keys(), values() oraz items(), które umożliwiają iterację po kluczach, wartościach i parach klucz-wartość.

Najpierw stwórzmy ponownie słownik:

nukleotydy = {
    'A': 'Adenina', 
    'C': 'Cytozyna', 
    'G': 'Guanina', 
    'T': 'Tymina',
    } 

Teraz iterujmy po kluczach:

for klucz in nukleotydy.keys():
    print(klucz)
A
C
G
T

Warto zauważyć, że ten sam efekt można osiągnąć bez metody keys():

for klucz in nukleotydy:
    print(klucz)
A
C
G
T

Oczywiście, skoro mamy klucz, można pobrać i wartość:

for klucz in nukleotydy.keys():
    print(f"symbol: {klucz}, znaczenie: {nukleotydy[klucz]}")
symbol: A, znaczenie: Adenina
symbol: C, znaczenie: Cytozyna
symbol: G, znaczenie: Guanina
symbol: T, znaczenie: Tymina

Iteracja po wartościach będzie wyglądała podobnie:

for wartosc in nukleotydy.values():
    print(wartosc)
Adenina
Cytozyna
Guanina
Tymina

Kolejne krotki z kluczami i wartościami pobierzemy z użyciem metody items():

for krotka in nukleotydy.items():
    print(krotka)
('A', 'Adenina')
('C', 'Cytozyna')
('G', 'Guanina')
('T', 'Tymina')

Możemy oczywiście uzyskać poszczególne elementy krotek:

for krotka in nukleotydy.items():
    print(f"symbol: {krotka[0]}, znaczenie: {krotka[1]}")
symbol: A, znaczenie: Adenina
symbol: C, znaczenie: Cytozyna
symbol: G, znaczenie: Guanina
symbol: T, znaczenie: Tymina

Jednak prościej i bardziej przejrzyście będzie to zrobić tak:

for klucz, wartosc in nukleotydy.items():
    print(f"symbol: {klucz}, znaczenie: {wartosc}")
symbol: A, znaczenie: Adenina
symbol: C, znaczenie: Cytozyna
symbol: G, znaczenie: Guanina
symbol: T, znaczenie: Tymina

Łączenie słowników

Metoda update() pozwala połączyć dwa słowniki:

nukleotydy = {
    'A': 'Adenina', 
    'C': 'Cytozyna', 
    'U': 'Uran',    
    } 

nukl_2 = {
    'G': 'Guanina', 
    'T': 'Tymina',
    'U': 'Uracyl', 
    } 

nukleotydy.update(nukl_2)
print(nukleotydy)
{'A': 'Adenina', 'C': 'Cytozyna', 'U': 'Uracyl', 'G': 'Guanina', 'T': 'Tymina'}

Zauważ, że jeśli w drugim słowniku znajduje się para z tym samym kluczem co w pierwszym, to go zastępuje.

Można połączyć dwa lub więcej słowników innym sposobem:

n_1 = {
    'A': 'Adenina', 
    'U': 'Uran',
    }

n_2 = {
    'C': 'Cytozyna', 
    'U': 'Uranium',  
    }

n_3 = {
    'G': 'Guanina', 
    'T': 'Tymina',
    'U': 'Uracyl', 
    } 

nukleotydy = {**n_1, **n_2, **n_3}

print(nukleotydy)

{'A': 'Adenina', 'U': 'Uracyl', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Kopiowanie słowników

Do kopiowania słowników, można użyć metody copy():

nukleotydy = {
    'A': 'Adenina', 
    'C': 'Cytozyna', 
    'G': 'Guanina', 
    'T': 'Tymina',
    } 
kopia = nukleotydy.copy()
print(f"{nukleotydy = }")
print(f"{kopia = }")
nukleotydy['U'] = 'Uracyl'
print(f"{nukleotydy = }")
print(f"{kopia = }")
nukleotydy = {'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}
kopia = {'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}
nukleotydy = {'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina', 'U': 'Uracyl'}
kopia = {'A': 'Adenina', 'C': 'Cytozyna', 'G': 'Guanina', 'T': 'Tymina'}

Ten sposób kopiowania ma ograniczenia, o czym będzie mowa w kolejnej lekcji.

Zbiory

Zbiory (ang. sets) są kolekcjami zawierającymi unikalne wartości. Przypominają zatem słowniki, ale z samymi kluczami. Podobnie też je tworzymy:

nukleotydy = {'A', 'C' , 'G', 'T'}
print(nukleotydy)
{'T', 'C', 'A', 'G'}

Pusty zbiór można utworzyć funkcją set():

zbior = set()
print(zbior)
print(type(zbior))
set()
<class 'set'>

Zastanów się, czemu utworzenie pustego zbioru z użyciem polecenia zbior = {} nie przyniesie zamierzonego efektu.

Funkcja set() umożliwia też utworzenie zbioru dzięki konwersji np. list, krotek, czy nawet łańcuchów znaków. Przy konwersji usuwane są zduplikowane elementy:

lista = [23, 22, 12, 3, 22]
zbior_1 = set(lista)
print(lista)
print(zbior_1)

lancuch = 'GTCCGATTACC'
zbior_2 = set(lancuch)
print(lancuch)
print(zbior_2)
[23, 22, 12, 3, 22]
{3, 12, 22, 23}
GTCCGATTACC
{'A', 'C', 'T', 'G'}

Zauważ, że jest to sposób uzyskania unikalnych wartości.

Inne operacje na zbiorach

Na zbiorach możemy też stosować takie funkcje i metody jak len(), add(), remove() czy copy():

planety = {'Merkury', 'Wenus', 'Ziemia', 'Mars', 'Jowisz', 'Saturn', 'Uran', 'Neptun'}
print(f"Planety: {planety}")
print(f"Liczba planet: {len(planety)}")
planety.add('Pluton')
print(f"Planety: {planety}")
planety.remove('Pluton')
print(f"Planety: {planety}")
planety_2 = planety.copy()
print(f"Kopia:   {planety_2}")
Planety: {'Jowisz', 'Saturn', 'Neptun', 'Mars', 'Ziemia', 'Wenus', 'Uran', 'Merkury'}
Liczba planet: 8
Planety: {'Jowisz', 'Saturn', 'Neptun', 'Mars', 'Ziemia', 'Pluton', 'Wenus', 'Uran', 'Merkury'}
Planety: {'Jowisz', 'Saturn', 'Neptun', 'Mars', 'Ziemia', 'Wenus', 'Uran', 'Merkury'}
Kopia:   {'Jowisz', 'Saturn', 'Neptun', 'Mars', 'Ziemia', 'Wenus', 'Uran', 'Merkury'}

Zauważ, że podobnie jak w przypadku słowników, kolejność elementów w zbiorze nie musi odpowiadać kolejności dodawania.

Iteracja elementów też wygląda podobnie:

for planeta in planety:
    print(planeta)
Merkury
Wenus
Ziemia
Neptun
Mars
Saturn
Uran
Jowisz

Tak można sprawdzić, czy dany element występuje w zbiorze:

if 'Pluton' in planety:
    print('Pluton jest planetą!')
else:
    print('Pluton nie jest planetą!')

Pluton nie jest planetą!

Niemodyfikowalne zbiory

Można też stworzyć zbiór, który już nie będzie mógł podlegać zmianom. Służy do tego funkcja frozenset():

planety = frozenset(['Merkury', 'Wenus', 'Ziemia', 'Mars', 'Jowisz', 'Saturn', 'Uran', 'Neptun'])
print(type(planety))
print(planety)
<class 'frozenset'>
frozenset({'Merkury', 'Wenus', 'Ziemia', 'Neptun', 'Mars', 'Saturn', 'Uran', 'Jowisz'})

Sprawdźmy, czy rzeczywiście zbioru nie można zmienić:

planety.add('Pluton')
----------------------------------------------------------

AttributeError           Traceback (most recent call last)

<ipython-input-136-b652a3251a9e> in <module>
----> 1 planety.add('Pluton')


AttributeError: 'frozenset' object has no attribute 'add'
Last updated on 22 Nov 2020
Published on 22 Nov 2020