Odcinek 3/15 – Struktury danych w Pythonie: listy, krotki, zbiory, słowniki

foto kurs pythona odcinek 3

Cel odcinka: nauczysz się przechowywać i porządkować dane w najważniejszych wbudowanych strukturach Pythona. Po lekturze swobodnie wybierzesz: listę, krotkę, zbiór albo słownik – i świadomie użyjesz właściwych metod.


1) Lista (list) – jak rząd pudełek w szafie

Co to jest? Uporządkowany, modyfikowalny ciąg elementów. Pamięta kolejność, może mieć duplikaty.

Kiedy używać? Gdy liczy się kolejność, chcesz dodawać/usuwać elementy i mogą się powtarzać.

Tworzenie i podstawy

nums = [10, 20, 30]
mix  = [1, "Ala", 3.14, True]

print(nums[0])    # 10  (indeksy od 0)
print(nums[-1])   # 30  (liczenie od końca)

Wycinki (slicing) – bierz kawałek listy

print(nums[0:2])  # [10, 20]
print(nums[:2])   # [10, 20]
print(nums[1:])   # [20, 30]
print(nums[::-1]) # [30, 20, 10] (odwrócona)

Dodawanie/usuwanie

a = [1, 2, 3]
a.append(4)                  # [1, 2, 3, 4]
a.extend([5, 6])             # [1, 2, 3, 4, 5, 6]
a.insert(0, 0)               # [0, 1, 2, 3, 4, 5, 6]
a.remove(3)                  # usuwa pierwszą 3
last = a.pop()               # usuwa i ZWRACA ostatni element
a.clear()                    # []

Sortowanie i kopiowanie (ważne!)

b = [3, 1, 2]
b.sort()                     # sortuje „na miejscu” -> [1, 2, 3]
c = sorted(b, reverse=True)  # nowa, posortowana -> [3, 2, 1]

x = [1, 2, 3]
y = x          # alias (ten sam obiekt!)
z = x.copy()   # kopia płytka
w = x[:]       # też kopia

Szybkie budowanie list – list comprehension

squares = [n*n for n in range(10)]             # [0, 1, 4, 9, ...]
even_sq  = [n*n for n in range(10) if n%2==0]  # tylko parzyste

Mini-zadanie: Zbuduj listę sześcianów liczb 1..10 i wypisz tylko te > 200.


2) Krotka (tuple) – paczka, której nie otwierasz

Co to jest? Uporządkowana, niemodyfikowalna sekwencja. Idealna na „stały zestaw wartości”.

Kiedy używać? Gdy dane nie powinny się zmieniać (np. współrzędne), albo funkcja zwraca kilka wartości.

point = (10, 20)
print(point[0])     # 10
# point[0] = 99     # TypeError – nie da się zmienić

a, b = (10, 20)     # rozpakowanie
print(a, b)         # 10 20

single = (42,)      # krotka jednoelementowa – przecinek jest konieczny

Mini-zadanie: Napisz funkcję, która przyjmuje dwie liczby i zwraca krotkę (suma, różnica, iloczyn).


3) Zbiór (set) – pudełko z unikalnymi kartami

Co to jest? Nieuporządkowana kolekcja unikalnych elementów. Zero duplikatów. Błyskawiczne sprawdzanie „czy jest?”.

Kiedy używać? Gdy chcesz usunąć duplikaty, szybko sprawdzać przynależność, robić operacje jak w matematyce (suma, część wspólna).

s = {1, 2, 2, 3}
print(s)         # {1, 2, 3} (duplikaty znikają)
print(2 in s)    # True

s.add(4)
s.update([3, 5])  # dodaj wiele naraz
s.discard(99)     # cicho, nawet jeśli nie ma
# s.remove(99)    # KeyError, jeśli nie ma

Operacje zbiorowe:

A = {1, 2, 3}
B = {3, 4, 5}

print(A | B)  # suma: {1, 2, 3, 4, 5}
print(A & B)  # część wspólna: {3}
print(A - B)  # różnica: {1, 2}
print(A ^ B)  # różnica symetryczna: {1, 2, 4, 5}

Mini-zadanie: Masz dwie listy e-maili, zduplikowane i pomieszane. Wypisz posortowaną listę unikalnych adresów.


4) Słownik (dict) – szuflada „nazwa → wartość”

Co to jest? Mapa klucz → wartość. Najczęściej kluczem jest napis. Szybki dostęp po nazwie.

Kiedy używać? Gdy chcesz łączyć dane z etykietami/polami (np. user.name, user.city).

user = {
    "name": "Ala",
    "age":  21,
    "city": "Gdańsk"
}

print(user["name"])              # Ala
print(user.get("email"))         # None (bez błędu)
print(user.get("email", "brak")) # 'brak'

user["email"] = "ala@example.com"  # dodaj/zmień
user.update({"age": 22})

user.pop("city")                 # usuń i zwróć wartość
user.pop("nie_ma", None)         # bez błędu, zwróci None

Iteracja i widoki:

for key in user.keys():
    print("KLUCZ:", key)

for value in user.values():
    print("WARTOŚĆ:", value)

for key, value in user.items():
    print(key, "=>", value)

Słownik w jedną linijkę (dict comprehension):

names   = ["ala", "ola", "ela"]
lengths = {n: len(n) for n in names}  # {"ala":3, "ola":3, "ela":3}

Mini-zadanie: Z listy osób [("Ala",21),("Ola",19)] zbuduj słownik {name: age}.


5) Praktyka – mini-projekty (krótkie i życiowe)

A) Deduplikacja e-maili + sortowanie

emails = ["a@x.pl","b@x.pl","a@x.pl","c@x.pl","b@x.pl"]
unique_sorted = sorted(set(emails))
print(unique_sorted)  # ['a@x.pl', 'b@x.pl', 'c@x.pl']

B) Licznik słów (prosty)

text = "Ala ma kota kot ma Ale"
words  = text.lower().split()
counts = {}

for w in words:
    counts[w] = counts.get(w, 0) + 1

print(counts)  # {'ala':1, 'ma':2, 'kota':1, 'kot':1, 'ale':1}

C) Książka kontaktów (słownik w słowniku)

contacts = {
    "Ala": {"tel": "111-222", "miasto": "Gdańsk"},
    "Ola": {"tel": "333-444", "miasto": "Warszawa"},
}

name = "Ala"
card = contacts.get(name)
if card:
    print(f"{name} – tel: {card['tel']}, miasto: {card['miasto']}")

D) Unikalne produkty z zachowaniem kolejności

products = ["piwo","wino","piwo","whisky","wino","rum"]
seen, unique = set(), []
for p in products:
    if p not in seen:
        seen.add(p)
        unique.append(p)
print(unique)  # ['piwo','wino','whisky','rum']

6) Typowe pułapki i proste obejścia

1) Modyfikowanie listy „w trakcie” iteracji – może pomijać elementy.
✔ Iteruj po kopii lub buduj nową listę.

a = [1, 2, 3, 4]
for x in a[:]:          # a[:] = kopia
    if x % 2 == 0:
        a.remove(x)

2) „Kopia” przez alias

a = [1, 2]
b = a        # to TEN SAM obiekt
b.append(3)
print(a)     # [1, 2, 3]
# prawdziwa kopia:
c = a.copy()

3) Mnożenie zagnieżdżonych list

grid = [[0]*3]*3   # trzy referencje do tej samej listy!
grid[0][0] = 1
print(grid)        # [[1,0,0],[1,0,0],[1,0,0]]  (zaskoczenie)

# poprawnie:
grid = [[0 for _ in range(3)] for _ in range(3)]

4) Zbiór nie ma kolejności – jeśli jej potrzebujesz, użyj listy albo sorted(set(...)).

5) Klucze słownika muszą być „niezmienne” (hashowalne): str, int, tuple.
list jako klucz → błąd.


7) Zadania do samodzielnego zrobienia (z podpowiedziami)

  1. Rejestr ocen
    Wczytaj imię i listę ocen (np. „5 4 3 5”). Policz średnią, min, max i wypisz raport.
    Tip: oceny = [int(x) for x in input().split()]
  2. Unikalne słowa
    Tekst na małe litery, usuń .,!?;: (np. replace), policz liczbę unikalnych słów i wypisz 10 najdłuższych.
    Tip: unikalne = set(slowa), potem sorted(unikalne, key=len, reverse=True)[:10].
  3. Częstotliwość liter
    Zlicz litery (bez spacji). Wynik w słowniku, wypisz malejąco po liczności.
    Tip: sorted(counts.items(), key=lambda x: x[1], reverse=True).
  4. Przecięcie list
    Dwie listy liczb → elementy wspólne (zbiory: &), potem posortuj.
    Tip: sorted(set(a) & set(b)).
  5. Odwróć mapę
    Masz {"Ala":"Gdańsk","Ola":"Warszawa","Ela":"Gdańsk"} → zrób {"Gdańsk":["Ala","Ela"],"Warszawa":["Ola"]} i posortuj listy imion.
    Tip: iteruj po items() i użyj dict.setdefault(miasto, []).append(imie).

Co zabierasz z odcinka?

  • Lista – gdy chcesz kolejność i modyfikacje.
  • Krotka – gdy dane mają być niezmienne lub funkcja zwraca kilka wartości.
  • Zbiór – gdy chcesz unikalność i szybkie „czy należy?”.
  • Słownik – gdy potrzebujesz dostępu po kluczu/nazwie pola.