Python Tuple a lista: podobieństwa i różnice, wyjaśnione

W tym artykule dogłębnie przeanalizujemy podobieństwa i różnice między krotkami a listami w języku Python. Zdobędziesz również wiedzę na temat tego, w jakich sytuacjach najbardziej efektywne jest użycie krotki.

Zarówno listy, jak i krotki stanowią integralne elementy języka Python, służąc jako wbudowane struktury danych. Ich podstawową funkcją jest gromadzenie i przechowywanie zbiorów elementów.

Mimo iż listy i krotki oferują zbliżoną funkcjonalność, obejmującą m.in. indeksowanie, wycinanie i możliwość przechowywania różnorodnych typów danych, ich zrozumienie kluczowych aspektów, które je różnicują, jest fundamentalne przy wyborze właściwej struktury danych.

Zacznijmy naszą analizę.

👩🏽‍💻 Zachęcamy do uruchomienia środowiska Python REPL i eksperymentowania z przykładami przedstawionymi w tym samouczku. Możesz także skorzystać z internetowego edytora Python newsblog.pl, aby pisać i uruchamiać kod.

Krotki kontra listy w Pythonie: Co je łączy?

Rozpocznijmy od omówienia podobieństw między listami a krotkami, posługując się przykładami, które pomogą w zrozumieniu omawianych koncepcji.

#1. Iteracja w Pythonie

W Pythonie, listy definiuje się za pomocą nawiasów kwadratowych `[]`, natomiast krotki za pomocą nawiasów okrągłych `()`. Krotki można również tworzyć poprzez proste oddzielenie wartości przecinkami, bez konieczności stosowania nawiasów.

Obie struktury są iterowalne, co oznacza, że można je przetwarzać za pomocą pętli `for`.

Poniższy fragment kodu pokazuje, jak można iterować przez listę.

nums = [2, 6, 7, 10]
print(f"Typ zmiennej nums to {type(nums)}")
for num in nums:
  print(num)

# Wynik
Typ zmiennej nums to <class 'list'>
2
6
7
10

Podobnie, jak pokazano poniżej, można iterować po krotce za pomocą pętli.

nums = (2, 6, 7, 10)

# Warto zauważyć: nums = 2, 6, 7, 10 też jest poprawną krotką.  W razie potrzeby można to sprawdzić.

print(f"Typ zmiennej nums to {type(nums)}")
for num in nums:
  print(num)

# Wynik
Typ zmiennej nums to <class 'tuple'>
2
6
7
10

#2. Tworzenie z istniejących sekwencji

Kolejnym podobieństwem jest możliwość tworzenia zarówno list, jak i krotek z istniejących sekwencji, takich jak łańcuchy znaków.

sample_str = "Kodowanie!"

Poniższy fragment kodu demonstruje, jak `list(string)` tworzy listę, której elementami są pojedyncze znaki łańcucha znaków.

list_from_str = list(sample_str)
print(list_from_str)

# Wynik
['K', 'o', 'd', 'o', 'w', 'a', 'n', 'i', 'e', '!']

Podobnie, krotka może być utworzona z łańcucha znaków lub innej sekwencji, za pomocą funkcji `tuple(sekwencja)`. Poniższy kod prezentuje, jak to zrobić.

tuple_from_str = tuple(sample_str)
print(tuple_from_str)

# Wynik
('K', 'o', 'd', 'o', 'w', 'a', 'n', 'i', 'e', '!')

#3. Indeksowanie i wycinanie

Python wspiera indeksowanie zerowe, gdzie pierwszy element ma indeks 0, drugi 1 i tak dalej. Umożliwia także indeksowanie ujemne, gdzie ostatni element ma indeks -1, przedostatni -2, itd.

list_from_str = ['K', 'o', 'd', 'o', 'w', 'a', 'n', 'i', 'e', '!']
print(list_from_str[1])
# o

Element o indeksie -2 to przedostatni element, 'e’.

tuple_from_str = ('K', 'o', 'd', 'o', 'w', 'a', 'n', 'i', 'e', '!')
print(tuple_from_str[-2])
# e

Wycinanie umożliwia operowanie na części listy lub krotki. Składnia `lista[start:end]` zwraca wycinek listy od indeksu `start` do indeksu `end` (bez elementu o indeksie `end`). Domyślna wartość `start` to 0, a domyślna wartość `end` to ostatni element struktury.

Krotki można wycinać za pomocą identycznej składni. Stwórzmy wycinki wcześniej utworzonej listy i krotki.

list_from_str = ['K', 'o', 'd', 'o', 'w', 'a', 'n', 'i', 'e', '!']
print(list_from_str[0:5])

# Wynik
['K', 'o', 'd', 'o', 'w']

Oprócz indeksów początkowego i końcowego można także określić krok. Składnia `krotka(start:end:step)` zwraca wycinek krotki od `start` do `end`-1, z krokiem `step`.

tuple_from_str = ('K', 'o', 'd', 'o', 'w', 'a', 'n', 'i', 'e', '!')
print(tuple_from_str[::2])

# Wynik
('K', 'd', 'w', 'n', 'e')

Tutaj ustawiamy krok na 2, dzięki czemu wycinek zawiera co drugi element.

#4. Zbiory różnorodnych typów danych

We wcześniejszych przykładach, wszystkie elementy list i krotek były tego samego typu.

W rzeczywistości, zarówno w listach, jak i w krotkach, można przechowywać elementy różnego typu danych.

W poniższym kodzie, `student_list` zawiera imię i nazwisko studenta jako łańcuch znaków, wiek jako liczbę całkowitą oraz wynik jako liczbę zmiennoprzecinkową.

student_list = ["Jan", 22, 96.5]
for item in student_list:
  print(f"{item} jest typu {type(item)}")

# Wynik
Jan jest typu <class 'str'>
22 jest typu <class 'int'>
96.5 jest typu <class 'float'>

Podobny przykład można utworzyć dla krotki.

student_tuple = ("Anna", 23, 99.5)
for item in student_tuple:
  print(f"{item} jest typu {type(item)}")

# Wynik
Anna jest typu <class 'str'>
23 jest typu <class 'int'>
99.5 jest typu <class 'float'>

#5. Sprawdzanie przynależności

Zarówno listy, jak i krotki umożliwiają testowanie przynależności do zbioru. Operator `in` pozwala sprawdzić, czy dany element znajduje się w liście lub krotce.

Wyrażenie `element in iterowalny` zwraca `True`, jeśli element znajduje się w strukturze, a w przeciwnym wypadku `False`.

"Alex" in student_list
# False

"Anna" in student_tuple
# True

Do tej pory poznaliśmy podobieństwa między listami a krotkami w Pythonie. Teraz przejdziemy do kluczowych różnic między tymi strukturami.

Krotki kontra listy w Pythonie: Co je różni?

#1. Zmienność vs. Niezmienność

Najważniejsza różnica między listami a krotkami w Pythonie to ich modyfikowalność. Krotki są niezmienne, co oznacza, że po utworzeniu nie można ich modyfikować.

▶️ Przykład:

tuple1 = ("Java", "Python", "C++")
tuple1[0] = "Rust"

# Wynik
----> 2 tuple1[0] = "Rust"

TypeError: 'tuple' object does not support item assignment

Listy są strukturami zmiennymi, co pozwala na modyfikację ich zawartości poprzez zmianę elementu na określonej pozycji, jak pokazano poniżej.

list1 = ["Java", "Python", "C++"]
list1[0] = "Rust"
print(list1)

# Wynik
['Rust', 'Python', 'C++']

#2. Zmienna długość list kontra stała długość krotek

Listy w Pythonie są strukturami o zmiennej długości.

Oznacza to, że możliwe jest:

  • Dodanie elementu na końcu listy
  • Dodanie elementów z innej listy na koniec bieżącej listy
  • Usunięcie elementów z określonego indeksu z listy
list1 = [2, 3, 4, 5]

# Dodanie elementu na końcu
list1.append(9)
print(list1)

# Dodanie elementów z list2 na koniec list1
list2 = [0, 7]
list1.extend(list2)
print(list1)

# Usunięcie elementu z list1
list1.pop(0)
print(list1)

▶️ Wynik powyższego kodu:

# Wynik
[2, 3, 4, 5, 9]
[2, 3, 4, 5, 9, 0, 7]
[3, 4, 5, 9, 0, 7]

Krotki są strukturami o stałej długości, co uniemożliwia dodawanie lub usuwanie elementów po ich utworzeniu. Możliwa jest jednak redefinicja krotki, aby zawierała nowe elementy.

tuple1 = (2, 4, 6, 8)
tuple1 = (1, 8, 9)
print(tuple1)

# Wynik
(1, 8, 9)

#3. Rozmiar w pamięci

Bazując na wiedzy z poprzedniej sekcji, wiemy, że listy są strukturami o zmiennej długości.

Po utworzeniu listy w pamięci, zostaje przydzielona jej określona ilość miejsca. Podczas modyfikacji listy za pomocą metod `append()` lub `extend()`, konieczne jest przydzielenie dodatkowej pamięci na nowe elementy. Alokacja ta jest zazwyczaj większa niż wymagana przestrzeń dla samych dodanych elementów.

W związku z tym, konieczne jest śledzenie liczby elementów na liście oraz przydzielonego miejsca. Dodatkowo, ze względu na zmienną długość, listy wymagają wskaźnika wskazującego na adres elementów. W rezultacie listy zajmują więcej pamięci niż krotki z identyczną ilością elementów.

Poniższa ilustracja to upraszcza.

Aby sprawdzić rozmiar obiektu w pamięci, można użyć wbudowanej metody `getsizeof()` z modułu `sys`.

import sys

list1 = [4, 5, 9, 14]
list_size = sys.getsizeof(list1)
print(f"Rozmiar listy: {list_size}")

tuple1 = (4, 5, 9, 14)
tuple_size = sys.getsizeof(tuple1)
print(f"Rozmiar krotki: {tuple_size}")

Jak potwierdza wynik, lista zajmuje więcej pamięci niż krotka dla tej samej liczby i wartości elementów.

# Wynik
Rozmiar listy: 104
Rozmiar krotki: 88

Kiedy używać krotki?

Po analizie różnic i podobieństw między listami i krotkami, wiemy, że listy są preferowane, gdy potrzebna jest modyfikowalna kolekcja.

W jakich sytuacjach najlepiej wykorzystać krotkę?

Zbadajmy to w tej sekcji.

#1. Kolekcje tylko do odczytu

Jeżeli potrzebna jest kolekcja, która ma być niezmienna, najlepiej zdefiniować ją jako krotkę. Przykładem może być krotka `kolor = (243, 55, 103)` reprezentująca wartości RGB. Użycie krotki gwarantuje, że wartości koloru nie zostaną zmodyfikowane.

Zasadniczo, krotki są idealne, gdy potrzebna jest kolekcja tylko do odczytu, która nie powinna być modyfikowana podczas działania programu, zapobiegając w ten sposób przypadkowym zmianom.

#2. Klucze słownika

Załóżmy, że chcesz stworzyć słownik, wykorzystując elementy listy `key_list` jako klucze. Możesz skorzystać z metody `dict.fromkeys()`.

key_list = list("ABCD")
dict.fromkeys(key_list)

# Wynik
{'A': None, 'B': None, 'C': None, 'D': None}

Załóżmy, że zmodyfikujesz listę, umieszczając „D” na pierwszej pozycji (indeks 0), przed utworzeniem słownika.

Co stanie się z kluczem słownika „A”?

Próba utworzenia słownika z `key_list` i odczytania wartości dla klucza „A” spowoduje błąd `KeyError`.

key_list[0] = 'D'
dict.fromkeys(key_list)['A']

# Wynik
KeyError                                  Traceback (most recent call last)
<ipython-input-31-c90392acc2cf> in <module>()
----> 1 dict.fromkeys(key_list)['A']

KeyError: 'A'

Klucze w słowniku muszą być unikatowe. Nie można mieć drugiego klucza „D”.

dict.fromkeys(key_list)

# Wynik
{'B': None, 'C': None, 'D': None} # A nie jest już kluczem.

Zastosowanie krotki eliminuje możliwość modyfikacji i minimalizuje ryzyko błędów. Dlatego krotki są preferowane jako klucze w słownikach.

key_tuple = tuple("ABCD")
dict.fromkeys(key_tuple)

# Wynik
{'A': None, 'B': None, 'C': None, 'D': None}
key_tuple[0] = 'D'

# Wynik
TypeError                                 Traceback (most recent call last)
<ipython-input-12-2cecbefa7db2> in <module>()
----> 1 key_tuple[0] = 'D'

TypeError: 'tuple' object does not support item assignment

#3. Argumenty funkcji

Niezmienność krotek czyni je idealnym wyborem jako argumenty funkcji.

Rozważmy funkcję `find_volume()`, która zwraca objętość prostopadłościanu o podanych wymiarach: długości, szerokości i wysokości.

def find_volume(dimensions):
  l, b, h = dimensions
  return l*b*h

Załóżmy, że wymiary są dostępne na liście o nazwie `dimensions`. Wywołanie `find_volume()` z `dimensions` jako argumentem zwraca objętość.

dimensions = [2, 8, 5]
find_volume(dimensions)

# Wynik
80

Zawartość listy można zawsze zmienić.

dimensions = [20, 8, 5]
find_volume(dimensions)

# Wynik
800

W pewnych sytuacjach, wartości powinny być niezmienne. Wówczas, warto przechowywać argumenty jako krotki i używać ich podczas wywoływania funkcji.

#4. Wartości zwracane przez funkcje

Krotki są często wykorzystywane jako wartości zwracane przez funkcje. W przypadku, gdy funkcja zwraca wiele wartości, Python niejawnie zwraca je w krotce.

Rozważmy funkcję `return_even()`:

def return_even(num):
  even = [i for i in range(num) if (i%2==0)]
  return even, len(even)
  • Przyjmuje liczbę jako argument.
  • Zwraca listę liczb parzystych w zakresie `[0, num)` i długość tej listy.

Ustawmy wartość `num` na 20 i wywołajmy funkcję.

num = 20

Wywołanie `return_even()` zwraca dwie wartości w krotce. Możesz to zweryfikować używając funkcji `type()`.

type(return_even(num))

# Wynik
<class 'tuple'>

Wyświetlając wartość zwracaną, można potwierdzić, że jest to krotka, zawierająca listę liczb parzystych jako pierwszy element oraz długość listy jako element drugi.

print(return_even(num))

# Wynik
([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], 10)

Ponieważ w krotce są dwa elementy, można je rozpakować do dwóch zmiennych, jak pokazano poniżej.

even_nums, count = return_even(num)

print(even_nums)
print(count)

# Wynik
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
10

Podsumowanie

Mamy nadzieję, że ten artykuł dostarczył wyczerpującego porównania krotek z listami w Pythonie.

Podsumujmy:

  • Listy i krotki są wbudowanymi strukturami danych w Pythonie.
  • Podobieństwa: iterowalne, obsługa indeksowania i wycinania, przechowywanie różnych typów danych, operator przynależności.
  • Kluczowa różnica: Listy są zmienne, a krotki niezmienne.
  • Dodatkowe różnice: Krotki mają stałą długość, listy zmienną, krotki zajmują mniej miejsca w pamięci.
  • Kiedy stosować krotki? Dla niezmiennych kolekcji, kluczy w słownikach i argumentów funkcji.

Zachęcamy do przejrzenia projektów w Pythonie, aby ćwiczyć i utrwalać wiedzę. Można również zapoznać się z metodami usuwania duplikatów z list. Życzymy owocnej nauki i udanego kodowania! 👩🏽‍💻


newsblog.pl