← Strona główna

07 — Słowniki

Pary klucz–wartość, tworzenie, metody, iterowanie, słowniki zagnieżdżone.

Struktury i pętle

1. Czym jest słownik?

Słownik (dict) to kolekcja par klucz–wartość. Pozwala szybko znaleźć wartość na podstawie klucza — jak w prawdziwym słowniku: szukasz słowa (klucz), znajdujesz definicję (wartość).

  • Tworzony w nawiasach klamrowych: {klucz: wartość, ...}
  • Klucze muszą być unikalne i niemutowalne (str, int, tuple)
  • Wartości mogą być dowolnego typu, mogą się powtarzać
  • Od Python 3.7 słowniki zachowują kolejność wstawiania
  • Dostęp przez klucz jest bardzo szybki — O(1)
  • Mutowalny — można dodawać, zmieniać i usuwać pary
Klucz musi być hashowalny Stringa, liczby i krotki mogą być kluczami. Lista jako klucz — nie! TypeError: unhashable type: 'list'. Wartości mogą być wszystkim, łącznie z listami.
Przykład — tworzenie słowników
# Słownik z literałem
uczen = {
    "imie":    "Kasia",
    "wiek":    17,
    "oceny":   [4, 5, 3, 5],
    "aktywny": True
}

# Pusty słownik
pusty = {}
pusty2 = dict()

# Ze słowa kluczowego dict()
osoba = dict(imie="Marek", wiek=20)

# Z listy par
pary  = [("a", 1), ("b", 2), ("c", 3)]
d     = dict(pary)
print(d)    # {'a': 1, 'b': 2, 'c': 3}

# Dict comprehension
kwadraty = {x: x**2 for x in range(1, 6)}
print(kwadraty)  # {1:1, 2:4, 3:9, 4:16, 5:25}

2. Dostęp do wartości i modyfikacja

Do wartości w słowniku odwołujesz się przez klucz w nawiasach kwadratowych. Możesz też użyć metody get() — bezpieczniejszej, bo nie powoduje błędu gdy klucz nie istnieje.

Operacja Składnia Uwaga
Odczyt d[klucz] KeyError gdy brak klucza
Bezpieczny odczyt d.get(klucz) zwraca None gdy brak
Odczyt z domyślną d.get(klucz, domyślna) zwraca domyślną gdy brak
Dodanie/zmiana d[klucz] = wartość nadpisuje jeśli istnieje
Usunięcie del d[klucz] KeyError gdy brak
Czy klucz istnieje klucz in d True/False
get() vs [] Na egzaminie gdy nie masz pewności czy klucz istnieje — używaj get(). Unikniesz KeyError który przerywa program.
Przykład — dostęp i modyfikacja
kontakt = {
    "imie":    "Zofia",
    "email":   "zofia@mail.pl",
    "telefon": "123456789"
}

# Odczyt
print(kontakt["imie"])          # Zofia
print(kontakt.get("email"))     # zofia@mail.pl
print(kontakt.get("wiek"))      # None — bez błędu!
print(kontakt.get("wiek", 0))   # 0 — wartość domyślna

# Sprawdzenie istnienia klucza
if "telefon" in kontakt:
    print(kontakt["telefon"])   # 123456789

# Dodanie nowego klucza
kontakt["miasto"] = "Kraków"
print(kontakt)

# Zmiana wartości
kontakt["email"] = "z.nowy@mail.pl"

# Usunięcie
del kontakt["telefon"]
print("telefon" in kontakt)     # False

3. Metody słownika

Metoda Działanie Zwraca
keys() wszystkie klucze dict_keys
values() wszystkie wartości dict_values
items() pary (klucz, wartość) dict_items
get(k, d) wartość lub domyślna wartość / None
pop(k) usuwa i zwraca wartość klucza k wartość
popitem() usuwa i zwraca ostatnią parę krotka
update(d2) dodaje/nadpisuje pary z d2 None
setdefault(k, v) zwraca wartość k lub ustawia v wartość
clear() usuwa wszystkie pary None
copy() płytka kopia słownika dict
keys(), values(), items() Zwracają widoki — aktualizują się automatycznie gdy słownik się zmienia. Jeśli potrzebujesz listy, owiń w list(): list(d.keys()).
Przykład — metody słownika
oceny = {"Kasia": 5, "Marek": 4, "Zofia": 3}

# Widoki
print(list(oceny.keys()))    # ['Kasia', 'Marek', 'Zofia']
print(list(oceny.values()))  # [5, 4, 3]
print(list(oceny.items()))
# [('Kasia', 5), ('Marek', 4), ('Zofia', 3)]

# pop — usuń i zwróć
o = oceny.pop("Marek")
print(o)       # 4
print(oceny)   # {'Kasia': 5, 'Zofia': 3}

# update — dodaj/nadpisz
nowe = {"Piotr": 5, "Kasia": 6}
oceny.update(nowe)
print(oceny)
# {'Kasia': 6, 'Zofia': 3, 'Piotr': 5}

# setdefault — ustaw jeśli brak
oceny.setdefault("Ola", 4)   # dodaje "Ola": 4
oceny.setdefault("Kasia", 1) # Kasia już jest — bez zmiany
print(oceny["Ola"])           # 4
print(oceny["Kasia"])         # 6

4. Iterowanie po słowniku

Słownik możesz przejść pętlą for na kilka sposobów. Najczęściej używane to iterowanie po parach przez items().

  • for k in slownik — iteruje po kluczach
  • for k in slownik.keys() — to samo, bardziej jawnie
  • for v in slownik.values() — iteruje po wartościach
  • for k, v in slownik.items() — klucz i wartość jednocześnie
Modyfikacja podczas iterowania Nie modyfikuj słownika (nie dodawaj/nie usuwaj kluczy) podczas iterowania po nim — spowoduje RuntimeError. Iteruj po kopii: for k in list(d.keys()).
Przykład — iterowanie po słowniku
punkty = {
    "Kasia": 87,
    "Marek": 92,
    "Zofia": 74,
    "Piotr": 88
}

# Po kluczach
for imie in punkty:
    print(imie, end=" ")
# Kasia Marek Zofia Piotr

# Po wartościach
for p in punkty.values():
    print(p, end=" ")
# 87 92 74 88

# Po parach — najczęściej używane
for imie, p in punkty.items():
    print(f"{imie}: {p} pkt")

# Znalezienie najlepszego
najlepszy = max(punkty, key=punkty.get)
print(f"Najlepszy: {najlepszy} ({punkty[najlepszy]} pkt)")

# Filtrowanie — tylko > 85
swietni = {k: v for k, v in punkty.items() if v > 85}
print(swietni)
# {'Kasia': 87, 'Marek': 92, 'Piotr': 88}

5. Dict comprehension

Analogicznie do list comprehension — zwięzły sposób tworzenia słowników.

  • Składnia: {klucz: wartość for x in sekwencja}
  • Z warunkiem: {k: v for k, v in d.items() if warunek}
  • Odwrócenie słownika: {v: k for k, v in d.items()}
  • Transformacja wartości: {k: f(v) for k, v in d.items()}
Odwracanie słownika {v: k for k, v in d.items()} zamienia klucze z wartościami. Działa poprawnie tylko gdy wartości są unikalne — inaczej niektóre klucze zostaną nadpisane.
Przykład — dict comprehension
# Kwadraty liczb
kwadraty = {x: x**2 for x in range(1, 8)}
print(kwadraty)
# {1:1, 2:4, 3:9, 4:16, 5:25, 6:36, 7:49}

# Filtrowanie par
ceny = {"jabłko": 2.5, "banan": 1.2, "mango": 8.9}
tanie = {k: v for k, v in ceny.items() if v < 5}
print(tanie)   # {'jabłko': 2.5, 'banan': 1.2}

# Transformacja wartości — zaokrąglenie
zaokr = {k: round(v) for k, v in ceny.items()}
print(zaokr)   # {'jabłko': 2, 'banan': 1, 'mango': 9}

# Odwrócenie słownika
dni   = {"pon": 1, "wt": 2, "sr": 3, "czw": 4}
odwr  = {v: k for k, v in dni.items()}
print(odwr)    # {1:'pon', 2:'wt', 3:'sr', 4:'czw'}

# Z listy — zliczanie wystąpień liter
tekst = "mississippi"
zlicz = {l: tekst.count(l) for l in set(tekst)}
print(zlicz)   # {'m':1, 'i':4, 's':4, 'p':2}

6. Słowniki zagnieżdżone

Wartością w słowniku może być inny słownik. To pozwala modelować złożone struktury danych — np. baza uczniów, konfiguracja programu.

  • Dostęp: d[klucz1][klucz2]
  • Bezpieczny dostęp: d.get(k1, {}).get(k2)
  • Modyfikacja: d[k1][k2] = nowa_wartość
  • Iterowanie: zagnieżdżone pętle lub rekurencja
Zastosowanie na egzaminie Słowniki zagnieżdżone pojawiają się w zadaniach z bazami danych uczniów, produktów czy konfiguracji. Ćwicz odczyt i modyfikację konkretnych pól.
Przykład — słowniki zagnieżdżone
uczniowie = {
    "u001": {
        "imie": "Kasia",
        "klasa": "3A",
        "oceny": [4, 5, 5, 3]
    },
    "u002": {
        "imie": "Marek",
        "klasa": "3B",
        "oceny": [3, 4, 2, 5]
    }
}

# Dostęp do zagnieżdżonych danych
print(uczniowie["u001"]["imie"])    # Kasia
print(uczniowie["u002"]["oceny"])   # [3, 4, 2, 5]

# Modyfikacja
uczniowie["u001"]["oceny"].append(4)

# Iterowanie po wszystkich uczniach
for uid, dane in uczniowie.items():
    sr = sum(dane["oceny"]) / len(dane["oceny"])
    print(f"{uid} | {dane['imie']:8} | "
          f"{dane['klasa']} | avg: {sr:.2f}")

# Dodanie nowego ucznia
uczniowie["u003"] = {
    "imie":  "Zofia",
    "klasa": "3A",
    "oceny": [5, 5, 4, 5]
}

Zadania przykładowe z omówieniem

Zadanie 1 Częstość słów w tekście łatwe

Wczytaj zdanie od użytkownika. Policz ile razy każde słowo wystąpiło w zdaniu (bez rozróżniania wielkich/małych liter). Wypisz słowa i ich liczby posortowane od najczęstszego.

Rozwiązanie
zdanie = input("Wpisz zdanie: ").lower()
slowa  = zdanie.split()

czestosci = {}
for slowo in slowa:
    czestosci[slowo] = czestosci.get(slowo, 0) + 1

posortowane = sorted(czestosci.items(),
                     key=lambda x: x[1],
                     reverse=True)

print("\nCzęstości słów:")
for slowo, ile in posortowane:
    print(f"  {slowo:<15} {ile}x")
Omówienie krok po kroku
  1. get() z wartością domyślną 0
    czestosci.get(slowo, 0) + 1 — jeśli słowo już jest w słowniku, pobierz jego licznik i dodaj 1. Jeśli nie ma — get zwraca 0, dodajemy 1. Elegancki wzorzec liczenia bez sprawdzania if slowo in.
  2. split() bez argumentu
    Dzieli po białych znakach (spacje, tabulatory, nowe linie) i automatycznie ignoruje wielokrotne spacje.
  3. Sortowanie par po wartości
    sorted(czestosci.items(), key=lambda x: x[1], reverse=True) — sortujemy pary (słowo, licznik) po drugim elemencie (x[1]) malejąco.
Zadanie 2 Sklep — koszyk zakupów średnie

Masz słownik z cenami produktów. Wczytuj od użytkownika nazwę produktu i ilość — dodawaj do koszyka. Wpisanie "koniec" kończy zakupy. Wypisz rachunek: każdy produkt, jego cenę, ilość i koszt, oraz łączną sumę.

Rozwiązanie
cennik = {
    "jabłko":  1.50,
    "banan":   0.80,
    "chleb":   3.20,
    "mleko":   2.90,
    "masło":   6.50
}

koszyk = {}   # {produkt: ilosc}

while True:
    produkt = input("Produkt (lub 'koniec'): ").lower()
    if produkt == "koniec":
        break
    if produkt not in cennik:
        print(f"Nie ma '{produkt}' w ofercie.")
        continue
    ilosc = int(input(f"Ile sztuk? "))
    koszyk[produkt] = koszyk.get(produkt, 0) + ilosc

print("\n--- RACHUNEK ---")
suma = 0
for produkt, ilosc in koszyk.items():
    koszt = cennik[produkt] * ilosc
    suma += koszt
    print(f"{produkt:<10} {ilosc:>3} szt. × "
          f"{cennik[produkt]:.2f} = {koszt:.2f} zł")
print(f"{'SUMA':<10} {suma:>18.2f} zł")
Omówienie krok po kroku
  1. Dwa słowniki — cennik i koszyk
    Cennik to stałe dane. Koszyk rośnie dynamicznie — dodajemy do niego produkty i ilości podczas zakupów.
  2. Sprawdzenie czy produkt istnieje
    if produkt not in cennik — zanim cokolwiek zrobimy, upewniamy się że produkt jest w ofercie. Bez tego — KeyError.
  3. Akumulacja ilości
    koszyk.get(produkt, 0) + ilosc — jeśli produkt już jest w koszyku (kupiony wcześniej), dodajemy do istniejącej ilości zamiast nadpisywać.
  4. Formatowanie rachunku
    :<10 wyrównuje do lewej, :>3 do prawej — kolumny są równe niezależnie od długości nazwy produktu.
Zadanie 3 Grupowanie po klasie średnie

Masz listę słowników z uczniami (imię i klasa). Grupuj uczniów według klasy — wynikiem ma być słownik gdzie kluczem jest klasa, a wartością lista imion uczniów. Wypisz każdą klasę z posortowaną listą imion.

Rozwiązanie
uczniowie = [
    {"imie": "Kasia",  "klasa": "3A"},
    {"imie": "Marek",  "klasa": "3B"},
    {"imie": "Zofia",  "klasa": "3A"},
    {"imie": "Piotr",  "klasa": "3C"},
    {"imie": "Ola",    "klasa": "3B"},
    {"imie": "Tomek",  "klasa": "3A"},
    {"imie": "Ania",   "klasa": "3C"},
]

klasy = {}
for u in uczniowie:
    klasa = u["klasa"]
    imie  = u["imie"]
    if klasa not in klasy:
        klasy[klasa] = []
    klasy[klasa].append(imie)

for klasa in sorted(klasy):
    lista = sorted(klasy[klasa])
    print(f"{klasa}: {', '.join(lista)}")
Omówienie krok po kroku
  1. Wzorzec "grupowania"
    Sprawdzamy czy klucz (klasa) już istnieje w słowniku. Jeśli nie — tworzymy pustą listę. Potem zawsze dodajemy imię. Ten wzorzec pojawia się bardzo często.
  2. setdefault() jako alternatywa
    Można też napisać: klasy.setdefault(klasa, []).append(imie) — to jedno wyrażenie zastępuje blok if. setdefault zwraca istniejącą lub nowo utworzoną wartość.
  3. Podwójne sortowanie
    sorted(klasy) — sortuje klucze słownika (nazwy klas) alfabetycznie. sorted(klasy[klasa]) — sortuje imiona w każdej klasie.
  4. join() do wypisania listy
    ', '.join(lista) łączy elementy listy w tekst rozdzielony przecinkami. Czytelniejsze niż drukowanie całej listy z nawiasami.

Zadania do samodzielnego rozwiązania

Słowniki są wszechstronne — ćwicz różne wzorce: zliczanie, grupowanie, odwracanie, filtrowanie.

1★☆☆

Tłumacz PL–EN

Stwórz słownik 10 słów polsko-angielskich. Wczytuj słowa od użytkownika i tłumacz je. Jeśli słowa nie ma — wypisz komunikat. Wpisanie "quit" kończy program.

Wskazówka: while True, get() z domyślną wartością
2★☆☆

Inwentaryzacja

Wczytuj pary: nazwa produktu i ilość. Jeśli produkt już istnieje — dodaj ilość. Wypisz końcowy stan magazynu posortowany alfabetycznie.

Wskazówka: get(k, 0) + ilosc, sorted(d.items())
3★★☆

Anagram

Napisz funkcję sprawdzającą czy dwa słowa są anagramami (zawierają te same litery w tej samej liczbie). Użyj słownika do zliczenia liter.

Wskazówka: zbuduj dwa słowniki {litera: count} i porównaj je
4★★☆

Ranking punktowy

Wczytuj imię i punkty gracza. Jeśli gracz już istnieje — sumuj punkty. Na koniec wypisz top 3 graczy z największą liczbą punktów.

Wskazówka: sorted(d.items(), key=lambda x: x[1], reverse=True)[:3]
5★★☆

Dict comprehension — zamiana

Masz słownik cen produktów w złotówkach. Używając dict comprehension utwórz nowy słownik z cenami w euro (kurs: 1 EUR = 4.25 PLN), zaokrąglonymi do 2 miejsc.

Wskazówka: {k: round(v/4.25, 2) for k, v in ...}
6★★★

Baza uczniów

Zbuduj słownik zagnieżdżony uczniów (id → dane). Zaimplementuj: dodaj ucznia, usuń ucznia, wypisz wszystkich posortowanych po nazwisku, oblicz średnią dla każdego.

Wskazówka: funkcje dla każdej operacji, sorted(d.items(), key=lambda x: x[1]['nazwisko'])