Python Tuple vs List: Podobnosti a rozdíly, vysvětleno

Tato příručka vám přiblíží podobnosti a odlišnosti mezi n-ticemi a seznamy v jazyce Python. Získáte také přehled o tom, kdy je vhodné použít n-tici.

Seznamy a n-tice jsou základní datové struktury, které Python nabízí. Slouží k uchovávání kolekcí prvků.

Přestože se na první pohled mohou zdát n-tice a seznamy velmi podobné, a to od možnosti indexování a řezů až po podporu různých datových typů, je mezi nimi několik zásadních rozdílů. Pochopení těchto nuancí vám pomůže při výběru nejvhodnější datové struktury pro váš konkrétní případ.

Pojďme se do toho ponořit.

👩🏽‍💻 Můžete si otevřít Python REPL a postupovat podle tohoto návodu krok za krokem. Alternativně můžete využít online editor Pythonu na etechblog.cz pro psaní a testování kódu.

Python Tuple vs List: Jaké jsou jejich společné rysy?

Nejprve se zaměříme na podobnosti mezi seznamy a n-ticemi. Pro lepší pochopení si ukážeme příklady pro oba typy datových struktur.

#1. Iterovatelnost

V Pythonu se seznamy uzavírají do hranatých závorek `[]`, zatímco n-tice jsou ohraničeny kulatými závorkami `()`. N-tici lze také definovat jako sekvenci hodnot oddělených čárkami, bez použití závorek.

Oba typy jsou iterovatelné, což znamená, že je můžete procházet pomocí smyčky `for`.

Následující ukázka kódu demonstruje iteraci seznamu:

nums = [2,6,7,10]
print(f"Typ proměnné nums je: {type(nums)}")
for num in nums:
  print(num)

# Výstup
Typ proměnné nums je: <class 'list'>
2
6
7
10

Jak ukazuje následující příklad, n-tici můžete také iterovat pomocí smyčky:

nums = (2,6,7,10)

# Poznámka: nums = 2,6,7,10 je také platná n-tice. Můžete si to rychle ověřit!

print(f"Typ proměnné nums je: {type(nums)}")
for num in nums:
  print(num)

# Výstup
Typ proměnné nums je: <class 'tuple'>
2
6
7
10

#2. Tvorba z jiných sekvencí

Další společný rys mezi seznamy a n-ticemi je, že je můžeme vytvářet z existujících sekvencí, například z řetězců.

sample_str = "Programování!"

Následující kód ukazuje, jak funkce `list(string)` vytvoří seznam, jehož prvky jsou jednotlivé znaky daného řetězce.

list_from_str = list(sample_str)
print(list_from_str)

# Výstup
['P', 'r', 'o', 'g', 'r', 'a', 'm', 'o', 'v', 'á', 'n', 'í', '!']

Podobně lze n-tici vytvořit z řetězce nebo jiné sekvence pomocí funkce `tuple(sekvence)`. Následující kód demonstruje tento postup.

tuple_from_str = tuple(sample_str)
print(tuple_from_str)

# Výstup
('P', 'r', 'o', 'g', 'r', 'a', 'm', 'o', 'v', 'á', 'n', 'í', '!')

#3. Indexování a řezy

Python používá nulové indexování, kde první prvek má index 0, druhý index 1 a tak dále. Python také podporuje záporné indexování, kde poslední prvek má index -1, předposlední index -2 atd.

list_from_str = ['P', 'r', 'o', 'g', 'r', 'a', 'm', 'o', 'v', 'á', 'n', 'í', '!']
print(list_from_str[1])
# r

Prvek na indexu -2 je předposlední, tedy ‚í‘.

tuple_from_str = ('P', 'r', 'o', 'g', 'r', 'a', 'm', 'o', 'v', 'á', 'n', 'í', '!')
print(tuple_from_str[-2])
# n

Řezy (slicing) umožňují pracovat s částmi seznamu nebo n-tice. `seznam[start:end]` vrátí část seznamu od indexu `start` až po `end – 1`. Výchozí hodnota pro `start` je 0 a pro `end` poslední prvek v sekvenci.

Stejnou syntaxi lze použít i pro n-tice. Vytvořme si řezy z dříve vytvořených seznamů a n-tic.

list_from_str = ['P', 'r', 'o', 'g', 'r', 'a', 'm', 'o', 'v', 'á', 'n', 'í', '!']
print(list_from_str[0:5])

# Výstup
['P', 'r', 'o', 'g', 'r']

Kromě počáteční a koncové hodnoty lze specifikovat i krok. `tuple[start:end:step]` vrátí část n-tice od indexu `start` po `end – 1`, s krokem `step`.

tuple_from_str = ('P', 'r', 'o', 'g', 'r', 'a', 'm', 'o', 'v', 'á', 'n', 'í', '!')
print(tuple_from_str[::2])

# Výstup
('P', 'o', 'r', 'm', 'v', 'n', '!')

V tomto případě je hodnota kroku 2, takže řez obsahuje každý druhý prvek.

#4. Kolekce různých datových typů

V dosavadních příkladech byly všechny prvky v seznamu i n-tici stejného datového typu.

V rámci jednoho seznamu nebo n-tice ale můžete ukládat hodnoty různých datových typů.

Následující fragment kódu ukazuje seznam `student_list`, který obsahuje jméno studenta jako řetězec, věk jako celé číslo a dosažené body jako desetinné číslo.

student_list = ["Jana",22,96.5]
for item in student_list:
  print(f"{item} má typ {type(item)}")

# Výstup
Jana má typ <class 'str'>
22 má typ <class 'int'>
96.5 má typ <class 'float'>

Podobný příklad můžeme vytvořit i pro n-tici.

student_tuple = ("Petr",23,99.5)
for item in student_tuple:
  print(f"{item} má typ {type(item)}")

# Výstup
Petr má typ <class 'str'>
23 má typ <class 'int'>
99.5 má typ <class 'float'>

#5. Testování členství

Seznamy i n-tice umožňují testovat, zda obsahují konkrétní položky. Pokud chcete ověřit, zda se daná položka nachází v seznamu nebo n-tici, můžete použít operátor `in`.

Výraz `položka in iterovatelný_objekt` se vyhodnotí jako `True`, pokud daný iterovatelný objekt obsahuje zadanou položku; v opačném případě jako `False`.

"Karel" in student_list
# False

"Petr" in student_tuple
# True

Zatím jsme se seznámili s podobnostmi mezi seznamy a n-ticemi v Pythonu. Nyní se podíváme na klíčové rozdíly mezi těmito dvěma datovými strukturami.

Python Tuple vs List: Jaké jsou jejich rozdíly?

#1. Proměnlivost seznamů a neměnnost n-tic

Zásadní rozdíl mezi seznamem a n-ticí v Pythonu spočívá v tom, že n-tice je neměnná. To znamená, že n-tici nelze přímo modifikovat.

▶️ Zde je příklad:

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

# Výstup
----> 2 tuple1[0] = "Rust"

TypeError: 'tuple' object does not support item assignment

Seznam je proměnlivá datová struktura, takže můžeme seznam upravovat změnou prvků na konkrétních indexech, jak ukazuje následující kód.

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

# Výstup
['Rust', 'Python', 'C++']

#2. Seznamy s proměnnou délkou vs. n-tice s pevnou délkou

Pythonovský seznam je datová struktura s proměnnou délkou.

Můžete provádět následující operace:

  • Přidat prvek na konec seznamu.
  • Přidat prvky z jiného seznamu na konec aktuálního seznamu.
  • Odstranit prvek na daném indexu ze seznamu.
list1 = [2,3,4,5]

# přidat prvek na konec
list1.append(9)
print(list1)

# přidat prvky z list2 na konec list1
list2 = [0,7]
list1.extend(list2)
print(list1)

# odstranit prvek z list1
list1.pop(0)
print(list1)

▶️ Výstup uvedeného fragmentu kódu.

# Výstup
[2, 3, 4, 5, 9]
[2, 3, 4, 5, 9, 0, 7]
[3, 4, 5, 9, 0, 7]

N-tice jsou datové struktury s pevnou délkou. Nemůžete tedy přidávat ani odebírat prvky z existující n-tice. Můžete ale n-tici předefinovat tak, aby obsahovala různé prvky.

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

# Výstup
(1, 8, 9)

#3. Velikost v paměti

Jak jsme si již řekli, seznam je datová struktura s proměnnou délkou.

Když definujete seznam, je mu v paměti přidělena určitá velikost. Když později seznam upravíte pomocí metod `append()` nebo `extend()`, musí být přidělena další paměť pro uložení přidaných prvků. Tato alokace se téměř vždy provádí ve větším rozsahu, než je počet přidávaných položek.

Proto je nutné sledovat počet prvků v seznamu a alokované paměťové místo. Navíc, protože seznamy mají proměnnou délku, existuje ukazatel na adresu prvků seznamu. Díky tomu seznamy o `k` prvcích zabírají více paměti než n-tice se stejnými `k` prvky.

Zde je jednoduché znázornění.

Velikost objektu v paměti zjistíte pomocí metody `getizeof()` vestavěného modulu `sys`.

import sys

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

tuple1 = (4,5,9,14)
tuple_size = sys.getsizeof(tuple1)
print(f"Velikost n-tice: {tuple_size}")

Seznam zabírá více paměti než n-tice se stejným počtem a hodnotami prvků, což potvrzuje následující výstup.

# Výstup
Velikost seznamu: 104
Velikost n-tice: 88

Kdy použít n-tici v Pythonu?

Na základě rozdílů a podobností mezi seznamy a n-ticemi v Pythonu víte, že pokud potřebujete proměnlivou kolekci, měli byste použít seznam.

Kdy byste ale měli použít n-tici?

Na to se podíváme v této části.

#1. Kolekce jen pro čtení

Pokud potřebujete, aby kolekce byla neměnná, definujte ji jako n-tici. Představte si, že `barva = (243,55,103)` je n-tice obsahující hodnoty RGB odpovídající barevnému odstínu. Definování barvy jako n-tice zajistí, že ji nelze upravit.

Zkrátka, kdykoli potřebujete, aby kolekce byla jen pro čtení – aby se hodnoty během běhu programu neměnily – měli byste zvážit použití n-tice. Zabráníte tak nechtěným změnám hodnot.

#2. Klíče slovníku

Například vytvoříte slovník pomocí položek seznamu `key_list` jako klíčů. K vytvoření slovníku ze seznamu můžete použít metodu `dict.fromkeys()`.

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

# Výstup
{'A': None, 'B': None, 'C': None, 'D': None}

Předpokládejme, že před vytvořením slovníku upravíte seznam tak, že první prvek (index 0) bude ‚D‘.

Co se stane s klíčem slovníku ‚A‘?

Pokud se pokusíte vytvořit slovník ze seznamu klíčů a pokusíte se získat hodnotu odpovídající klíči ‚A‘, narazíte na chybu `KeyError`.

key_list[0] = 'D'

dict.fromkeys(key_list)['A']

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

KeyError: 'A'

Klíče slovníku by měly být jedinečné. Nemůžete mít víc klíčů se stejnou hodnotou, například druhé ‚D‘.

dict.fromkeys(key_list)

# Výstup
{'B': None, 'C': None, 'D': None} # A už není klíč.

Pokud místo toho použijete n-tici, taková úprava není možná, a je tedy méně pravděpodobné, že narazíte na chyby. Proto byste měli preferovat vytváření slovníků s klíči, které jsou prvky n-tice.

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

# Výstup
{'A': None, 'B': None, 'C': None, 'D': None}

key_tuple[0] = 'D'

# Výstup
---------------------------------------------------------------------------
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 funkcí

Neměnnost n-tic je také činí vhodnými pro předávání jako argumenty funkcím.

Vezměte v úvahu následující funkci `find_volume()`, která vypočítá objem kvádru na základě jeho rozměrů: délky, šířky a výšky.

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

Předpokládejme, že tyto rozměry jsou uložené v seznamu nazvaném `dimensions`. Voláním `find_volume()` s `dimensions` jako argumentem získáme objem.

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

# Výstup
80

Rozměry uložené v seznamu lze kdykoli změnit.

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

# Výstup
800

Někdy je ale potřeba, aby hodnoty zůstaly konstantní a neměnné. V takovém případě byste měli uvažovat o uložení argumentů jako n-tice a jejich použití ve volání funkce.

#4. Návratové hodnoty z funkcí

V Pythonu se s n-ticemi často setkáváme jako s návratovými hodnotami z funkcí. Když z funkce vrátíte více hodnot, Python je implicitně vrátí jako n-tici.

Vezměte si následující funkci `return_even()`:

def return_even(num):
  even = [i for i in range(num) if (i%2==0)]
  return even,len(even)
  • Funkce přijímá jako argument číslo `num`.
  • Vrací seznam sudých čísel v intervalu [0, num) a délku tohoto seznamu.

Nastavme hodnotu `num` na 20 a zavolejme funkci.

num = 20

Volání `return_even()` vrátí obě hodnoty v n-tici. Typ návratové hodnoty ověříme pomocí funkce `type()`, která jako argument přijímá volání funkce.

type(return_even(num)) # <class 'tuple'>

Výstup můžeme zkontrolovat a ověřit, že je to n-tice obsahující seznam sudých čísel jako první prvek a délku seznamu jako druhý prvek.

print(return_even(num))
# Výstup
([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], 10)

Vzhledem k tomu, že v n-tici jsou dvě položky, můžeme je rozbalit do dvou proměnných, jak je ukázáno níže.

even_nums, count = return_even(num)

print(even_nums)
print(count)

# Výstup
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
10

Závěr

Doufám, že tento návod poskytl komplexní srovnání seznamu s n-ticí v Pythonu.

Zakončeme tento tutoriál rychlým shrnutím:

  • Seznamy a n-tice jsou vestavěné datové struktury v Pythonu.
  • Podobnosti: iterovatelnost, podpora indexování, řezy, různé datové typy a operátor pro testování členství.
  • Klíčový rozdíl: Seznamy jsou proměnlivé a n-tice jsou neměnné.
  • Další rozdíly: Pevná délka n-tic a proměnná délka seznamů, menší velikost n-tic v paměti.
  • Kdy použít n-tici? Pro neměnné kolekce, klíče slovníku a argumenty funkcí.

Dále se podívejte na projekty Pythonu, abyste si mohli procvičit své nově nabyté dovednosti. Můžete se také podívat na metody, jak odstranit duplicitní položky ze seznamů. Přejeme vám hodně štěstí při učení i kódování! 👩🏽‍💻