Seznamy
Dnes si ukážeme, jak pracovat se seznamy
(angl. lists).
Doufám že víš, kde máš na klávesnici hranaté
závorky, protože právě těmi se seznamy vytváří:
fibonacciho_cisla = [1, 1, 2, 3, 5, 8, 13]
print(fibonacciho_cisla)
Seznam je hodnota, která může obsahovat spoustu
dalších hodnot.
Tak jako řetězec obsahuje sekvenci znaků,
seznam obsahuje sekvenci jakýchkoli hodnot.
A tak jako můžeme pomocí cyklu for
procházet řetězec po znacích,
seznam můžeme procházet po jednotlivých prvcích:
for cislo in fibonacciho_cisla:
print(cislo)
Seznamy se v programech vyskytují velice často:
soubor se dá načíst jako seznam řetězců
s jednotlivými řádky,
seznam řetězců jako '7♥'
a 'K♣'
poslouží jako balíček karet,
matematika je plná číselných řad,
každá online služba má seznam uživatelů.
Hodnoty v seznamu můžou být jakéhokoli typu,
dokonce můžeme různé typy míchat v jednom seznamu
(i když s takovými namixovanými seznamy se
příliš často nesetkáme):
seznam = [1, 'abc', True, None, range(10), len]
print(seznam)
Vybírání ze seznamů
Nejzákladnější operaci se seznamy,
cyklus for
, už jsme si ukázaly;
druhá nejdůležitější operace je vybírání
prvků seznamu.
To funguje jako u řetězců: do hranatých závorek
se dá číslo prvku. Čísluje se, jako u řetězců,
od nuly; záporná čísla označují prvky od konce.
print(fibonacciho_cisla[2])
Hranatými závorkami můžeme získávat podseznamy.
Diagram z materiálů k řetězcům
ukazuje, jak u takového „sekání” číslovat;
funguje to stejně, jen místo menšího řetězce
dostaneme menší seznam.
print(fibonacciho_cisla[2:-3])
Měnění seznamů
Důležitá vlastnost seznamů,
kterou nemají ani čísla, ani řetězce
(a True/False/None už vůbec ne), je,
že seznamy se dají měnit.
Čísla měnit nejdou – máme-li a = 3
a
napíšeme a = a + 1
;
číslo 3
se nezmění.
Vypočítá se nové
číslo 4
a proměnná a
se nastaví na toto nové číslo.
Oproti tomu seznamy se dají měnit bez nastavování
proměnné.
Základní způsob, jak změnit seznam, je přidání
prvku na konec pomocí metody append
.
Ta nic nevrací (resp. vrací None),
ale „na místě” (angl. in place) změní
seznam, na kterém pracuje:
prvocisla = [2, 3, 5, 7, 11, 13, 17]
print(prvocisla)
prvocisla.append(19)
print(prvocisla)
Takové měnění hodnoty může být občas překvapující,
protože stejnou hodnotu může mít více proměnných.
Protože se mění hodnota samotná, můžeme „změnit
proměnnou” aniž na ni „sáhneme”:
a = [1, 2, 3]
b = a
print(b)
a.append(4)
print(b)
Další způsoby, jak měnit seznamy
Kromě metody append
, která přidává
jediný prvek, existuje metoda extend
,
která umí přidávat prvků víc.
Prvky k přidání ji předáme ve formě seznamu:
dalsi_prvocisla = [23, 29, 31]
prvocisla.extend(dalsi_prvocisla)
print(prvocisla)
Metoda extend
umí pracovat i s jinými
typy než se seznamy – ráda zpracuje cokoli, přes
co umí cykilt for
, tedy např.
jednotlivé znaky řetězců, řádky souborů, nebo čísla
z range()
.
seznam = []
seznam.extend('abcdef')
seznam.extend(range(10))
print(seznam)
Měnění prvků
Ale dost přidávání.
Seznamům se dají i měnit jednotlivé prvky,
a to jednoduše tak, že do prvku přiřadíme
jako by to byla proměnná:
cisla = [1, 0, 3, 4]
cisla[1] = 2
print(cisla)
Přiřazovat se dá i do podseznamu – v tomto případě
se podseznam nahradí jednotlivými prvky z toho,
co přiřazujeme.
Jako u extend
,
do podseznamu můžeme opět přiřadit cokoli, co umí
zpracovat for
– seznam, řetězec,
range()
apod.
cisla = [1, 2, 3, 4]
cisla[1:-1] = [6, 5]
print(cisla)
Mazání prvků
Přiřazením do podseznamu se dá i změnit délka
seznamu, nebo některé prvky úplně odstranit:
cisla = [1, 2, 3, 4]
cisla[1:-1] = [0, 0, 0, 0, 0, 0]
print(cisla)
cisla[1:-1] = []
print(cisla)
Tenhle zápis pro mazání prvků je ale docela
nepřehledný, a proto na to máme zvláštní příkaz
jménem del
.
Jak už jeho název (z angl. delete, smazat)
napovídá, smaže, co mu přijde pod ruku – jednotlivé
prvky seznamů, podseznamy, … a dokonce i proměnné.
cisla = [1, 2, 3, 4, 5, 6]
del cisla[-1]
print(cisla)
del cisla[3:5]
print(cisla)
del cisla
print(cisla)
Další mazací metody jsou:
-
pop
, která odstraní a vrátí
poslední prvek v seznamu – například pokud
mám seznam karet v balíčku, jde takhle
jednoduše „líznout” kartu,
-
remove
, která najde v seznamu
daný prvek a odstraní ho,
-
clear
, která vyprázdní celý
seznam.
cisla = [1, 2, 3, 'abc', 4, 5, 6, 12]
posledni = cisla.pop()
print(posledni)
print(cisla)
cisla.remove('abc')
print(cisla)
cisla.clear()
print(cisla)
Řazení
A taky tu máme metodu sort
,
která prvky seznamu seřadí.
seznam = [4, 7, 8, 3, 5, 2, 4, 8, 5]
seznam.sort()
print(seznam)
Aby se daly seřadit, musí být prvky seznamu
porovnatelné – konktrétně na ně musí fungovat
operátor <
.
Seznam s mixem čísel a řetězců tedy
seřadit nepůjde.
Operátor <
definuje i
jak přesně se řadí (např. čísla podle velikosti;
řetězce podle speciální „abecedy” která řadí
velká písmena za malá, česká až za anglická, atd.).
Metoda sort
zná pojmenovaný argument
reverse
. Pokud ho nastavíme
na true, řadí se „naopak”.
seznam = [4, 7, 8, 3, 5, 2, 4, 8, 5]
seznam.sort(reverse=True)
print(seznam)
Známé operace se seznamy
Spousta toho, co můžeme dělat s řetězci, má stejný
účinek i u seznamů.
Třeba sečítání a násobení číslem:
melodie = ['C', 'E', 'G'] * 2 + ['E', 'E', 'D', 'E', 'F', 'D'] * 2 + ['E', 'D', 'C']
print(melodie)
Stejně jako u řetězců, sečítat jde jen seznam
se seznamem – ne třeba seznam s řetězcem.
Další staří známí jsou funkce len
,
metody count
a index
,
a operátor in
.
print(len(melodie)) # Délka seznamu
print(melodie.count('D')) # Počet 'D' v seznamu
print(melodie.index('D')) # Číslo prvního 'D'
print('D' in melodie) # Je 'D' v seznamu?
Poslední tři se ale přece jen chovají kapku jinak:
u řetězců pracují s podřetězci,
u seznamů jen s jednotlivými prvky.
Takže ačkoliv naše melodie obsahuje prvky
'D'
a 'E'
vedle sebe, 'DE'
v seznamu není:
print('DE' in melodie)
print(melodie.count('DE'))
print(melodie.index('DE'))
Seznam jako podmínka
Seznam se dá použít v příkazu if
(nebo while
) jako podmínka,
která platí když v tom seznamu něco je.
Jinými slovy, seznam
je tu zkratka pro
len(seznam) > 0
.
if seznam:
print('V seznamu něco je!')
else:
print('Seznam je prázdný!')
Podobně se dají v podmínce použít i řetězce.
A dokonce i čísla –
ty jako podmínka platí, pokud jsou nenulová.
Tvoření seznamů
Tak jako funkce int
převádí na
celá čísla a str
na řetězce,
funkce list
(angl. seznam)
převádí na seznam.
Jako argument jí předáme jakoukoli hodnotu,
kterou umí zpracovat příkaz for
.
Z řetězců udělá seznam znaků, z otevřeného souboru
udělá seznam řádků, z range
udělá
seznam čísel.
abeceda = list('abcdefghijklmnopqrstuvwxyz')
cisla = list(range(100))
print(abeceda)
print(cisla)
I ze seznamu udělá funkce list
seznam.
To může znít zbytečně, ale není – vytvoří se
totiž nový seznam.
Bude mít sice stejné prvky ve stejném pořadí,
ale nebude to ten samý seznam:
měnit se bude nezávisle na tom starém.
a = [1, 2, 3]
b = list(a)
print(b)
a.append(4)
print(b)
Další způsob, jak tvořit seznamy
(zvláště složitější), je nejdřív udělat prázdný
seznam, a pak ho postupně naplnit pomocí funkce
append
.
Třeba pokud z nějakého důvodu chceš seznam
mocnin dvou, projdi čísla kterými chceme mocnit
cyklem for
, a pro každé z nich
do seznamu přidej příslušnou mocninu:
mocniny_dvou = []
for cislo in range(10):
mocniny_dvou.append(2 ** cislo)
print(mocniny_dvou)
Chceš-li seznam, který reprezentuje balíček karet,
zavolej append
pro všechny kombinace
barev a hodnot.
balicek = []
for barva in '♠', '♥', '♦', '♣':
for hodnota in list(range(2, 11)) + ['J', 'Q', 'K', 'A']:
balicek.append(str(hodnota) + barva)
print(balicek)
Seznamy a řetězce
Seznamy a řetězce jsou druhy „sekvencí”,
takže snad nepřekvapí, že se dá různě převádět
z jednoho typu na druhý.
Funkce list
vytvoří z řetězce
seznam znaků.
Když chceme dostat seznam slov, použijeme
na řetězci metodu split
(angl. rozdělit):
slova = 'Tato věta je složitá, rozdělme ji na slova!'.split()
print(slova)
Metoda split
umí brát i argument.
Pokud ho předáme, místo mezer (a nových řádků)
se řetězec „rozseká” daným oddělovačem.
Takže když máme nějaká data oddělená čárkami,
není nic jednoduššího než použít split
s čárkou:
zaznamy = '3A,8B,2X,9D'.split(',')
print(zaznamy)
Chceme-li spojit seznam řetězců zase dohromady,
do jediného řetězce, použijeme metodu
join
(angl. spojit).
Pozor, tahle metoda se volá na oddělovači,
tedy řetězci, kterým se jednotlivé kousky „slepí”
dohromady; a jako argument bere seznam jednotlivých
řetězců.
veta = ' '.join(slova)
print(veta)
Úkol
Některé z vás pro 1-D piškvorky udělali funkci
tah_pocitace
, která umí hrát jen
za křížky.
Takové funkce se nedají přímo použít v turnaji,
kde jeden z hráčů musí hrát za kolečka.
Jedno z možných řešení je napsat funkci, která
zamění v řetězci všechna 'x'
za
'o'
a naopak.
print(zamen_xo('---xo--xooxxox-'))
Pak se dá napsat funkce, která pokud má hrát
za kolečka,
- zamění v hracím poli 'x'↔'o',
- na výsledek zavolá funkci, která zahraje křížek,
- a ve výsledku opět zamění 'x'↔'o'.
Původní křížky a kolečka tak zůstanou na svých
místech, ale nově přidaný křížek se nahradí
za kolečko.
def tah_pocitace(pole, symbol):
if symbol == 'x':
return tah_pocitace_x(pole)
else:
return zamen_xo(tah_pocitace_x(zamen_xo(pole)))
Zkus napsat funkci zamen_xo
.
Udělej v ní seznam jednotlivých znaků
(viz sekce Tvoření seznamů),
a pak ho převéď na řetězec pomocí
join
.
Řešení
def zamen_xo(retezec):
vysledek = []
for znak in retezec:
if znak == 'o':
vysledek.append('x')
elif znak == 'x':
vysledek.append('o')
else:
vysledek.append(znak)
return ''.join(vysledek)
Prázdný oddělovač způsobí, že se jednotlivé
prvky seznamu „nalepí” těsně vedle sebe.
Seznamy a náhoda
Modul random
obsahuje dvě funkce,
které se hodí k seznamům.
Jako random.randrange
, obě mají něco
společného s náhodou.
Funkce shuffle
seznam „zamíchá” –
všechny prvky náhodně popřehází.
Jako metoda sort
, i funkce
shuffle
nic nevrací.
import random
balicek = []
for barva in '♠', '♥', '♦', '♣':
for hodnota in list(range(2, 11)) + ['J', 'Q', 'K', 'A']:
balicek.append(str(hodnota) + barva)
print(balicek)
random.shuffle(balicek)
print(balicek)
A funkce choice
ze seznamu vybere
jeden náhodný prvek.
S použitím seznamu tak můžeme výrazně zjednodušit
úvodní část naší staré hry kámen/nůžky/papír :
import random
mozne_tahy = ['kámen', 'nůžky', 'papír']
tah_pocitace = random.choice(mozne_tahy)
N-tice
Když už známe seznam, podívejme se na jeho
mladší sesrřičku: takzvanou n-tici
(angl. tuple).
N-tice, podobně jako seznam,
může obsahovat n prvků.
N-tice se dvěma prvky je dvojice
(angl. pair); se třemi prvky
trojice (angl. 3-tuple),
se čtyřmi čtveřice (angl. 4-tuple),
atd.
Existují i n-tice s jedním prvkem
(jednice?)
a nula prvky (prázdné n-tice, angl. empty tuple),
ale těmi se ze začátku nebudeme zabývat.
N-tice se tvoří jako seznamy, jen kolem sebe
nemají hranaté závorky.
Stačí čárky mezi prvky.
Chovají skoro stejně jako seznamy,
jen nejdou měnit.
Nemají tedy metody jako append
a pop
, a nedá se jim přiřazovat
do prvků.
Dají se ale použít v cyklu for
,
a dájí se z nich brát jednotlivé prvky.
osoby = 'máma', 'teta', 'babička'
for osoba in osoby:
print(osoba)
print('První je {}'.format(osoby[0]))
Když chceme n-tici předat funkci,
narazíme na problém, že čárka odděluje jednotlivé
argumenty funkce.
V takových případech musíme n-tici
uzavřít do závorky, aby bylo jasné že jde o jednu
hodnotu (byť složenou).
seznam_dvojic = []
for i in range(10):
seznam_dvojic.append((i, i**2))
print(seznam_dvojic)
N-tice se hodí, pokud chceme z funkce vrátit
víc než jednu hodnotu.
Prostě v příkazu return
oddělíme
vracené hodnoty čárkou.
Vypadá to, že vracíme několik hodnot, ale
ve skutečnosti vrací jen jedna n-tice.
def podil_a_zbytek(a, b):
return a // b, a % b
Python umí ještě jeden trik: pokud chceme přiřadit
do několika proměnných najednou, stačí je na levé
straně rovnítka oddělit čárkou, a na pravou stranu
dát nějakou „složenou” hodnotu – třeba právě
n-tici.
podil, zbytek = podil_a_zbytek(12, 5)
N-tice se k tomuto účelu hodí nejvíc, ale
jde to se všemi hodnotami, které jdou použít ve
for
:
x, o = 'xo'
jedna, dva, tri = [1, 2, 3]
Funkce, které vracejí n-tice
zip
je zajímavá funkce.
Používá se ve
for
cyklech, podobně
jako funkce
range
která „dává” čísla.
Když funkce
zip
dostane dva seznamy
(či jiné věci použitelné ve
for
),
„dává” dvojice, a to tak, že nejdřív spáruje
první prvek jednoho seznamu s prvním prvkem
druhého seznamu,
pak druhý s druhým, třetí s třetím a tak dál.
Hodí se to, když máme dva seznamy se stejnou
strukturou – příslušné prvky k sobě „patří”,
a chceme je zpracovávat společně:
osoby = 'máma', 'teta', 'babička', 'vrah'
vlastnosti = 'hodná', 'milá', 'laskavá', 'zákeřný'
for osoba, vlastnost in zip(osoby, vlastnosti):
print('{} je {}'.format(osoba, vlastnost))
Když zip
dostane tři seznamy,
bude tvořit trojice, ze čtyř seznamů nadělá
čtveřice, a tak dále.
Další funkce, která vrací dvojice, je
enumerate
.
Jako argument bere seznam (či jinou věc použitelnou
ve for
), a vždy spáruje index
(pořadí v seznamu) s příslušným prvkem.
Jako první tedy dá
(0, první prvek seznamu), potom
(1, první prvek seznamu),
(2, třetí prvek seznamu),
a tak dále.
prvocisla = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
for i, prvocislo in enumerate(prvocisla):
print('Pvočíslo č.{} je {}'.format(i, prvocislo))
Kdy použít seznam, a kdy n-tici?
Seznamy se používají, když předem nevíme,
kolik v nich přesně bude hodnot,
nebo když je hodnot mnoho.
Například seznam slov ve větě,
seznam účastníků soutěže, seznam tahů ve hře,
nebo seznam karet v balíčku.
Oproti tomu for pozdrav in 'Ahoj', 'Hello', 'Hola', 'Hei', 'SYN':
používá n-tici.
N-tice se často používají na hodnoty
různých typů, kdy má každá „pozice”
v n-tici úplně jiný význam.
Například seznam můžeme použít na písmena abecedy,
ale dvojice index–hodnota z enumerate
je n-tice.
Seznamy i n-tice mají i technické limity:
n-tice nejdou měnit,
a až se naučíme pracovat se slovníky,
zjistíme že seznamy tam nepůjdou použít jako klíče.
Často ale není úplně jasné, který typ použít
– v takovém případě je to pravděpodobně jedno.
Řiď se instinktem :)
Vnořené seznamy
A perlička na konec!
Na začátku tohoto textu je napsáno, že v seznam
může obsahovat jakýkoli typ hodnot.
Samozřejmě může obsahovat i další seznamy:
seznam_seznamu = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Takový seznam se chová docela normálně – jdou
z něj třeba brát jednotlivé prvky
(které jsou ovšem taky seznamy):
prvni_seznam = seznam_seznamu[0]
print(prvni_seznam)
A protože jsou prvky samy seznamy,
můžeme mluvit o věcech jako „první prvek
druhého seznamu”:
druhy_seznam = seznam_seznamu[1]
prvni_prvek_druheho_seznamu = druhy_seznam[0]
print(prvni_prvek_druheho_seznamu)
A protože výraz seznam_seznamu[1]
označuje seznam, můžeme brát prvky přímo z něj:
prvni_prvek_druheho_seznamu = (seznam_seznamu[1])[0]
Neboli:
prvni_prvek_druheho_seznamu = seznam_seznamu[1][0]
A má tahle věc nějaké použití, ptáš se?
Stejně jako vnořené cykly for
nám umožnily vypsat tabulku, vnořené seznamy
nám umožní si tabulku „zapamatovat”.
def vytvor_tabulku(velikost=11):
seznam_radku = []
for a in range(velikost):
radek = []
for b in range(velikost):
radek.append(a * b)
seznam_radku.append(radek)
return seznam_radku
nasobilka = vytvor_tabulku()
print(nasobilka[2][3])
print(nasobilka[5][2])
print(nasobilka[8][7])
for radek in nasobilka:
for cislo in radek:
print(cislo, end=' ')
print()