Kurs Python · Moduł 2: Struktury danych
Krotki i zbiory - niezmienne zapisy i unikalne gatunki
W tej lekcji4
Witaj! Na ekspedycji trafiasz na dane, dla których lista nie jest najlepszym wyborem. Pierwszy przykład to współrzędne wodopoju: para liczb, która nigdy nie powinna się zmienić. Gdyby jakaś funkcja przez pomyłkę nadpisała jedną z nich, cały zespół szukałby wody w złym miejscu. Drugi przykład to rejestr gatunków widzianych dzisiaj. Zebra przeszła obok obozu pięć razy, ale to wciąż jeden gatunek, a lista zapisze ją pięciokrotnie.
Python ma na te sytuacje dwa narzędzia. Krotka (tuple) to zapis, którego nie da się zmienić. Zbiór (set) to kolekcja, w której każdy element występuje tylko raz.
Krotka - wpis atramentem
Lista przypomina notatki ołówkiem, które możesz zmazywać i dopisywać. Krotka to wpis atramentem w dzienniku: raz zapisany, zostaje na zawsze. Tworzysz ją nawiasami okrągłymi, a elementy oddzielasz przecinkami:
1waterhole = (-2.33, 34.83)
2lion_record = ("Lew", 190, True)
3print(waterhole[0]) # -2.33
4print(lion_record[1]) # 190Indeksowanie działa dokładnie tak jak w liście, od zera. Krotka może też mieszać typy danych: lion_record przechowuje nazwę, wagę i informację, czy zwierzę jest niebezpieczne.
Przecinek tworzy krotkę
Jest jeden szczegół, na którym potyka się wielu początkujących: to przecinek, a nie nawiasy, tworzy krotkę. Najlepiej widać to przy krotce z jednym elementem, sprawdzonej funkcją type() z pierwszego modułu:
1single = ("Lew",)
2not_a_tuple = ("Lew")
3print(type(single)) # <class 'tuple'>
4print(type(not_a_tuple)) # <class 'str'>Bez przecinka Python widzi zwykły tekst w nawiasach, takich samych jak w działaniach matematycznych. Z tego samego powodu zapis point = 10.5, 20.3 również tworzy krotkę. Nawiasy są wtedy opcjonalne, ale polecam zawsze je pisać, bo od razu widać, że to krotka.
Niezmienność
Spróbujmy poprawić współrzędną wodopoju tak, jak zrobilibyśmy to w liście:
1waterhole = (-2.33, 34.83)
2waterhole[0] = -2.40 # TypeError!Python zgłasza błąd TypeError, bo krotki nie da się zmienić po utworzeniu. To nie wada, tylko gwarancja: współrzędne nie zmienią się przez literówkę w innej części programu. Dlatego krotka nie ma metod append(), remove() ani sort(). Zostały jej tylko dwie metody, które czytają dane: count() i index().
Rozpakowywanie krotki
Krotkę z rekordem możesz jednym ruchem rozłożyć na osobne zmienne. Po lewej stronie znaku = podajesz tyle nazw, ile elementów ma krotka:
1name, weight, dangerous = lion_record
2print(name) # Lew
3print(weight) # 190Każda zmienna dostała element z tej samej pozycji, a sama krotka została bez zmian. Jeśli liczba nazw nie zgadza się z liczbą elementów, Python zgłosi ValueError. Rozpakowywanie znasz zresztą z pętli for key, value in catalog.items(), bo metoda items() podaje właśnie krotki z kluczem i wartością.
Kiedy więc krotka, a kiedy lista? Moja zasada: jeśli dane opisują jedną rzecz o stałej budowie, jak współrzędne albo rekord zwierzęcia, wybierz krotkę. Jeśli to kolekcja, która rośnie i maleje, jak dzisiejsze obserwacje, wybierz listę.
Zbiór - rejestr unikalnych gatunków
Wróćmy do zebry, która odwiedziła obóz pięć razy. Zbiór działa jak lista obecności przy wodopoju: każdy gatunek odhaczasz raz, nawet jeśli przyszedł pić dziesięć razy. Najprościej zbudujesz go, przepuszczając listę przez funkcję set():
1sightings = ["Zebra", "Lew", "Zebra", "Słoń", "Zebra"]
2species = set(sightings)
3print(species) # {'Lew', 'Zebra', 'Słoń'}
4print(len(species)) # 3Duplikaty zniknęły, a lista sightings nadal ma pięć wpisów, bo funkcja set() zbudowała nową kolekcję. Kolejność na wydruku może być u Ciebie inna, ponieważ zbiór nie pamięta kolejności elementów. Z tego powodu nie ma w nim indeksów i species[0] zakończy się błędem.
Tworzenie i zmienianie zbioru
Zbiór możesz też zapisać wprost, w nawiasach klamrowych. Uwaga na pułapkę: puste nawiasy {} tworzą pusty słownik, a nie zbiór, więc pusty zbiór zapisujesz jako set().
1visitors = {"Zebra", "Lew"}
2visitors.add("Słoń")
3visitors.add("Zebra")
4print(len(visitors)) # 3
5print("Lew" in visitors) # TrueW przeciwieństwie do krotki zbiór jest mutowalny. Metoda add() dopisała słonia, ale druga zebra niczego nie zmieniła, bo już była w rejestrze. Operator in sprawdza obecność bardzo szybko, nawet w ogromnym zbiorze. Do usuwania służą dwie metody: remove(), która przy braku elementu zgłasza błąd, oraz discard(), która wtedy po prostu nic nie robi. Przy niepewnych danych polecam discard().
Operacje na zbiorach
Prawdziwą siłę zbiorów widać dopiero wtedy, gdy porównujesz dwa rejestry. Rano i wieczorem zespół notuje gatunki przy wodopoju, a trzy operatory odpowiadają na trzy różne pytania:
1morning = {"Zebra", "Lew", "Słoń"}
2evening = {"Zebra", "Hipopotam", "Słoń"}
3print(morning & evening) # {'Zebra', 'Słoń'}
4print(morning | evening) # {'Zebra', 'Lew', 'Słoń', 'Hipopotam'}
5print(morning - evening) # {'Lew'}Operator & to część wspólna, czyli gatunki widziane rano i wieczorem. Operator | to suma: wszystko, co pojawiło się w ciągu dnia, bez powtórzeń. Operator - to różnica, czyli gatunki z rana, których wieczorem zabrakło. Żaden z nich nie zmienia zbiorów morning ani evening, każdy buduje nowy zbiór z wynikiem. Przy różnicy liczy się kolejność: evening - morning da {'Hipopotam'}. Na liczbach wszystko działa tak samo, więc {1, 2, 3} & {2, 3, 4} zwraca {2, 3}.
Którą strukturę wybrać?
Znasz już cztery struktury danych i każda ma swoje miejsce w ekwipunku badacza:
- lista - uporządkowana i zmienna, dopuszcza duplikaty, jak dziennik obserwacji,
- krotka - uporządkowana i niezmienna, jak współrzędne albo stały rekord,
- zbiór - bez kolejności i bez duplikatów, jak rejestr unikalnych gatunków,
- słownik - pary klucz i wartość, jak katalog odkryć z kartami.
Gdy potrzebujesz unikalnych elementów, wybieraj zbiór zamiast ręcznego pilnowania duplikatów w liście.
Pamiętaj: krotka to wpis atramentem, którego nie zmienisz, a zbiór to rejestr, w którym każdy gatunek występuje tylko raz. W następnej lekcji Darwin pokaże Ci list comprehensions, a w projekcie na końcu modułu krotki i zbiory pomogą Ci przeanalizować dziennik ekspedycji.
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Czym krotka (tuple) różni się od listy?
2. Jaka jest główna cecha zbioru (set)?
3. Co zwraca: {1, 2, 3} & {2, 3, 4}?
Zadania praktyczne w grze
- Edytor kodu
Utwórz krotkę 'coordinates' z wartościami: 10.5, 20.3, 15.7
- Edytor kodu
Usuń duplikaty z listy numbers = [1, 2, 2, 3, 3, 3, 4] funkcją set(), zapisz wynik w zmiennej unique i wyświetl go
- Układanie w poziomie
Ułóż elementy w prawidłowej kolejności:
- Klikanie w kolejności
Kliknij elementy w prawidłowej kolejności:
- Układanie w pionie
Uporządkuj kroki: