... ale báli jste se zeptat na hodinÄ ;)
from utils import TableOfContents
TableOfContents("python_crash_course.ipynb")
Objekty¶
Programovánà v Pythonu spoÄÃvá v manipulaci s různými objekty -- ÄÃsli, ÅetÄzci (pÃsmen), seznamy, funkcemi... ZastÅeÅ¡ujÃcà pojem "objekt" naznaÄuje analogii s fyzickými pÅedmÄty. Každý pÅedmÄt sloužà k nÄÄemu jinému, nÄco s nÃm lze udÄlat jednoduÅ¡e, nÄco hůÅ, nÄco vůbec; taky pÅÃpadnÄ může obsahovat dalšà pÅedmÄty. PodobnÄ v Pythonu: ÄÃsla se hodà na sÄÃtánÃ, ale nemůžeme zmÄÅit jejich délku; a seznam jakožto objekt je vlastnÄ kolekcà dalÅ¡Ãch objektů, jeho prvků.
Když Python spustÃte, naÄte se pomÄrnÄ velké množstvà zabudovaných objektů (funkcà a konstant), se kterými můžete rovnou zaÄÃt pracovat. NapÅ. funkce sorted vytvoÅà z libovolného iterovatelného objektu (= objektu, ze kterého lze jeden po druhém tahat nÄjaké jeho dÃlÄà prvky) seÅazený seznam:
# cokoli za znakem # na jakémkoli Åádku Python ignoruje,
# jsou to tzv. komentáÅe, které sloužà jen lidem
sorted
(Seznam všech zabudovaných funkcà -- built-in functions -- v Pythonu)
Konstanta True zas oznaÄuje "pravdu" (v logickém smyslu)...
True
... takže ji Python vrátà jako výslednou hodnotu pÅi vyhodnocenà pravdivého výroku:
2 + 2 == 4
Ale dalšà objekty si můžeme libovolnÄ vytváÅet.
# ÄÃsla
42
# ÅetÄzce
"Hello, world!"
Proměnné¶
Abychom s objekty mohli dál pracovat, je potÅeba je nÄjak pojmenovat, jinak na nÄ nemůžeme odkazovat. Å tÃtek se jménem můžeme na objekt povÄsit pomocà pÅiÅazovacÃho operátoru =.
vzdálenost = 10
Äas = 20
rychlost = vzdálenost / Äas
rychlost
TÄm Å¡tÃtkům se jmény se ÅÃká promÄnné, protože je lze libovolnÄ kdykoli povÄsit na jiný objekt.
vzdálenost = 5
rychlost = vzdálenost / Äas
rychlost
Jsou i jiné způsoby, jak povÄsit Å¡tÃtek se jménem na nÄjaký objekt. NapÅ. když teÄ vytvoÅÃme funkci a pojmenujeme ji rychlost, tak Å¡tÃtek rychlost strhneme z ÄÃsla 0.25, na které jsme ho naposledy navÄsili, a nalepÃme ho na tu funkci (která je z hlediska Pythonu zase jen dalÅ¡Ãm typem objektu, jako ÄÃsla, ÅetÄzce atp.).
def rychlost(vzdálenost, Äas):
return vzdálenost / Äas
rychlost
Nynà tedy už promÄnná rychlost neodkazuje na ÄÃslo 0.25, ale na funkci, kterou jsme právÄ vytvoÅili.
Funkci můžeme zavolat, tj. spustit "recept" (sérii kroků, malý dÃlÄà program), který je v nà uložený:
rychlost(vzdálenost, Äas)
Nic nebránà tomu, abychom na existujÃcà objekty navÄsili Å¡tÃtků vÃc, a můžeme k tomu použÃt stávajÃcà štÃtky:
distance = vzdálenost
time = Äas
speed = rychlost
speed(distance, time)
Jak to funguje? Python nejprve vyhodnotà výraz napravo od operátoru =, tj. v tomto pÅÃpadÄ jen dohledá objekty, na které odkazujà původnà promÄnné, a pak na ty samé objekty navÄsà dalšà nové Å¡tÃtky (uvedené nalevo od operátoru =).
VÅ¡imnÄte si, že jsme tÃmto způsobem jednoduÅ¡e pÅiÅadili dalšà jméno i funkci rychlost:
speed
To, zda dvÄ jména (dvÄ promÄnné) odkazujà na ten samý objekt, jednoduÅ¡e zjistÃme pomocà operátoru is:
distance is vzdálenost
distance is Äas
distance = Äas
distance is Äas
speed is rychlost
POZN.: Rovnost (x == y) a identita (x is y) jsou dvÄ různé vÄci. Dva různé objekty (z hlediska identity) jsou si rovné, pokud reprezentujà tu samou hodnotu. NapÅ. dva různé seznamy ÄÃsel od jedné do tÅÃ:
x = [1, 2, 3]
y = [1, 2, 3]
x == y
x is y
Je to podobnÄ jako s jednovajeÄnými dvojÄaty: vypadajà stejnÄ, takže tÅeba pro úÄel focenà jsou zamÄnitelná, ale to neznamená, že je to ten samý ÄlovÄk.
JeÅ¡tÄ pár slov ke konvencÃm pojmenovávánà promÄnných v Pythonu:
- u drtivé vÄtÅ¡iny promÄnných (vÄ. funkcÃ) je zvykem použÃvat jen malá pÃsmena, napÅ.
promÄnná - obÄas se použÃvajà i ÄÃslice, napÅ.
x1, ale jméno promÄnné ÄÃslicà nesmà zaÄÃnat - když pojmenovánà sestává z vÃce slov, oddÄlujà se pomocà podtržÃtka, napÅ.
vÃceslovná_pojmenovánÃ(tzv. snake case) - promÄnné, které se použÃvajà napÅÃÄ celým programem (Äasto je definujeme na zaÄátku programu a pak na nÄ odkazujeme v růzých jeho Äástech), se nÄkdy odliÅ¡ujà pomocà velkých pÃsmen, napÅ.
ABECEDAneboRYCHLOST_SVÄTLA - jména tÅÃd (viz nÞe) jsou vÄtÅ¡inou v tzv. camel case s velkým poÄáteÄnÃm pÃsmenem, napÅ.
TÅÃdaÄiJináTÅÃda
Jméno promÄnné by nemÄlo být moc dlouhé, ale zároveÅ by mÄlo být deskriptivnà -- mÄlo by srozumitelnÄ naznaÄovat, k Äemu promÄnná sloužÃ, jak se použÃvá. Hezky a trefnÄ pojmenovat promÄnnou je Äasto tÄžšÃ, než by se na prvnà pohled mohlo zdát. Když pÃÅ¡ete program na jedno použitÃ, je to jedno, ale pokud po sobÄ budete program jeÅ¡tÄ nÄkdy ÄÃst (nebo nÄkdo jiný), budete za každou smysluplnÄ pojmenovanou promÄnnou (a každý vhodnÄ umÃstÄný komentáÅ) vdÄÄnÃ.
Typy a třídy¶
Každý objekt v Pythonu spadá do nÄjaké kategorie objektů -- napÅ. ÄÃslo, ÅetÄzec, seznam apod. -- podobnÄ jako pÅedmÄty v reálném svÄtÄ spadajà do různých kategorià (vÅ¡echny koÄky do kategorie "koÄka", vÅ¡echny tužky do kategorie "tužka" apod.). TÄmto kategoriÃm s v Pythonu ÅÃká typy a u libovolného objektu lze zjistit jeho typ pomocà funkce type:
obj = 1
type(obj)
obj = "ahoj"
type(obj)
obj = [1, "ahoj"]
type(obj)
type(1) == type(2)
type("a") == type("b")
# ovšem pozor
type("1")
type(1) == type("1")
Typy jako int, str nebo list jsou v Pythonu pÅÃmo zabudované, jsou k dispozici vždycky. Ale každý programátor si může definovat vlastnà typy a dále s nimi pracovat. TÄmto uživatelsky definovaným typům se ÅÃká tÅÃdy, podle klÃÄového slovÃÄka class, pomocà nÄhož se definujÃ:
class FooBar:
# klÃÄové slovÃÄko pass je prázdný pÅÃkaz (Python na jeho základÄ nic
# nevykoná)
pass
Instanci tÅÃdy, tj. konkrétnà objekt, jehož typem bude daná tÅÃda, vytvoÅÃme tak, že zavoláme tzv. konstruktor, tj. funkci, která nám instanci "postavÃ", "zkonstruuje". PoÄáteÄnà sestavenà a nastavenà objektu nÄkdy nazýváme inicializacÃ. Jako konstruktor vÄtÅ¡inou sloužà tÅÃda samotná, kterou zavoláme jako funkci:
obj = FooBar()
type(obj)
type(obj) is FooBar
Oproti tomu instance zabudovaných typů můžeme vytváÅet i pomocà speciálnÃch zápisů, tzv. literálů, nepotÅebujeme k tomu nutnÄ funkce. V konstruktoru je proces vytváÅenà objektu svým způsobem skrytý, nenà na prvnà pohled jasné, co se v nÄm odehrává (obecnÄ může konstruktorem být libovolná funkce), kdežto v literálu doslova (angl. literally) prostÄ jen vypÃÅ¡eme, jak má nÄjaký objekt vypadat, a Python nám ho podle toho poskládá:
# ÄÃselný literál
42
# ÅetÄzcový literál
"Hello, world!"
Literály jsou souÄástà syntaxe Pythonu, tj. pravidel pro skládánà programů tak, aby jim Python rozumÄl a mohl je vykonávat. Důležité jsou jeÅ¡tÄ literály pro vytváÅenà kolekcà (viz odd. Kolekce).
Chceme-li ovÄÅit, zda je nÄjaký objekt instancà nÄjaké dané tÅÃdy Äi obecnÄji typu, můžeme použÃt funkci isinstance.
isinstance(obj, FooBar)
isinstance(1, int)
TÅÃdy se hodà pro nÄkteré pokroÄilejšà programátorské úÄely (viz napÅ. notebook object_oriented.ipynb). V tomto semestru se jim blÞe nevÄnujeme.
Importování knihoven¶
Knihovna (též balÃÄek, angl. package) je v programovánà soubor funkcÃ, tÅÃd atp., které jsou Äasto užiteÄné, takže nemá smysl, aby si je každý programátor implementoval znovu a znovu, když je potÅebuje. ZároveÅ ale nejsou potÅeba úplnÄ poÅád, takže taky nemá smysl, aby byly do Pythonu pÅÃmo zabudované (tj. aby byly k dispozici pokaždé, když Python spustÃme, podobnÄ tÅeba jako funkce sorted). Vždy jsou ale na dosah ruky, staÄà je importovat.
NapÅ. knihovna random obsahuje funkcionalitu souvisejÃcà s generovánÃm náhodných ÄÃsel:
import random
# následujÃcÃmu pÅÃkazu nenà potÅeba vÄnovat pÅÃliÅ¡nou
# pozornost, jen zajistÃ, abychom pokaždé dostali stejnou
# sérii pseudonáhodných ÄÃsel; viz:
# https://docs.python.org/3/library/random.html#random.seed
random.seed(42)
K objektům, které knihovna definuje, se pak dostaneme pÅes teÄku. NapÅ. pokud chceme pomocà funkce randint vygenerovat náhodné ÄÃslo mezi jednou a deseti:
random.randint(1, 10)
PÅi importu si lze knihovnu pÅejmenovat pomocà klÃÄového slovÃÄka as, napÅ. pokud jméno random už v programu použÃváme pro nÄco jiného.
random = "random. angl. náhodný"
import random as rnd
rnd.randint(1, 10), random
Taky je možné si z knihovny naimportovat pÅÃmo jeden konkrétnà objekt (funkci, tÅÃdu atp.):
from random import randint
randint(1, 10)
To se hodà zejm. v pÅÃpadÄ, kdy objekt budeme použÃvat Äasto a nechce se nám pokaždé zdlouhavÄ psát jméno_knihovny.jméno_objektu. Pokud chceme, můžeme si jich rovnou naimportovat i vÃc zároveÅ:
from random import random, randint
random(), randint(1, 10)
VÄtšà knihovny sestávajà z dÃlÄÃch modulů, které pÅi importu oddÄlujeme teÄkami.
import os.path
this_file = "python_crash_course.ipynb"
os.path.isfile(this_file)
import os.path as osp
osp.realpath(this_file)
from os.path import splitext
splitext(this_file)
Funkce¶
Funkce je jako recept -- série pÅÃkazů, malý dÃlÄà program, který má jméno, pomocà nÄhož ho můžeme kdykoli spusit, tzv. zavolat. Funkci definujeme pomocà klÃÄového slovÃÄka def...
def hoÄ_kostkou(): # hlaviÄka
return randint(1, 6) # tÄlo
... a voláme tak, že napÃÅ¡eme jejà jméno a za nÄ kulaté závorky:
hoÄ_kostkou()
Recept uložený ve funkci se vykonává krok po kroku (Åádek po Åádku), dokud Python nenarazà na klÃÄové slovÃÄko return. V tu chvÃli funkci ukonÄà a hodnotu výrazu, který se nacházà za return, vrátà jako výsledek celé funkce. Tento výsledek můžeme uložit do promÄnné:
výsledek_hodu_kostkou = hoÄ_kostkou()
výsledek_hodu_kostkou
Do tÄla funkce patÅà vÅ¡e, co následuje po hlaviÄce a je odsazené alespoÅ o jednu úroveÅ dál než samotná hlaviÄka:
def function():
print("Tohle patÅÃ do tÄla funkce.")
print("Tohle taky.")
print("Tohle už ne.")
function()
Funkce Äasto majà jeden nebo vÃce parametrů Äi argumentů, tj. promÄnných, které deklarujeme v hlaviÄce funkce. NapÅ. následujÃcà funkce má parametry poÄet_stÄn a násobek.
def hoÄ_kostkou_a_vynásob(poÄet_stÄn, násobek):
return randint(1, poÄet_stÄn) * násobek
Když funkci voláme, je potÅeba za parametry dosadit konkrétnà objekty, které funkce pak v rámci receptu použije:
hoÄ_kostkou_a_vynásob(20, 100)
POZN.: PÅÃsnÄ vzato bychom mÄli takto rozliÅ¡ovat mezi (formálnÃmi) parametry (= promÄnnými, se kterými pracujeme pÅi definici funkce a které v tu chvÃli nereprezentujà žádnou konkrétnà hodnotu) a (konkrétnimi) argumenty (= konkrétnÃmi objekty, které funkci pÅedáme ve chvÃli, kdy ji voláme). NicménÄ Äasto tento rozdÃl nenà pÅÃliÅ¡ důležitý, takže v praxi se oba termÃny použÃvajà zamÄnitelnÄ.
Parametry mohou mÃt defaultnà hodnotu:
def hoÄ_kostkou_a_vynásob(poÄet_stÄn, násobek=100):
return randint(1, poÄet_stÄn) * násobek
Pokud nám defaultnà hodnota daného parametru vyhovuje, nemusÃme ho pak pÅi volánà specifikovat:
hoÄ_kostkou_a_vynásob(20)
Pokud nám naopak nevyhouje, nic nám v tom nebránÃ:
hoÄ_kostkou_a_vynásob(20, 0.01)
Parametrům bez defaultnà hodnoty se ÅÃká povinné (je potÅeba je specifikovat vždy), parametrům s defaultnà hodnotou pak volitelné.
Parametry můžeme funkci pÅedávat buÄ na základÄ poÅadà (tzv. poziÄnÄ)...
# poÄet_stÄn = 4, násobek = 10
hoÄ_kostkou_a_vynásob(4, 10)
# poÄet_stÄn = 10, násobek = 4
hoÄ_kostkou_a_vynásob(10, 4)
... nebo pomocà jejich jmen (pak hovoÅÃme o pojmenovaných argumentech, angl. named nebo též keyword arguments):
hoÄ_kostkou_a_vynásob(poÄet_stÄn=4, násobek=10)
# v tomto pÅÃpadÄ na poÅadà nezáležÃ
hoÄ_kostkou_a_vynásob(násobek=10, poÄet_stÄn=4)
Oba způsoby můžeme i kombinovat, dokonce je to velmi Äasté. V Pythonu je vÄtÅ¡inou zvykem, že se povinné parametry pÅedávajà poziÄnÄ (bývá jich málo a je potÅeba je zadávat pokaždé, takže si ÄlovÄk jejich poÅadà zapamatuje) a volitelné parametry jsou pojmenované (může jich být mnoho a použÃvajà se pÅÃležitostnÄ, takže si ÄlovÄk jejich poÅadà nezapamatuje; navÃc Äasto chceme specifikovat jen jeden a ostatnÃm nechat defaultnà hodnotu). V pÅÃpadÄ našà funkce by to vypadalo takto:
hoÄ_kostkou_a_vynásob(4, násobek=10)
Lokální vs. globální proměnné¶
Parametry plus jakékoli dalšà promÄnné, které v rámci funkce vytvoÅÃme, jsou soukromé, dostupné ÄistÄ jen funkci -- jsou to tzv. lokálnà promÄnné. Též hovoÅÃme o tom, že tyto promÄnné majà lokálnà dosah (angl. local scope). To znamená, že s nimi můžeme pracovat pouze v rámci tÄla funkce.
def function(parameter):
local_variable = 1
print("My parameter:", parameter)
print("My local variable:", local_variable)
function(0)
Mimo tÄlo funkce function nejsou promÄnné parameter a local_variable definované:
parameter
local_variable
TIP: NauÄit se ÄÃst chybové hlášky je k nezaplacenÃ, Python se vám s jejich pomocà snažà ze vÅ¡ech sil poradit, v Äem je problém. Zkuste si každou chybovou hláškou peÄlivÄ pÅeÄÃst, zamyslet se nad nÃ, pochopit, v Äem je problém a kde pÅesnÄ nastal.
Funkce mohou taky pracovat s globálnÃmi promÄnnými, tj. s promÄnnými definovanými samostatnÄ, mimo nÄjaké funkce Äi tÅÃdy.
global_variable = 2
def function(parameter):
local_variable = 1
print("My parameter:", parameter)
print("My local variable:", local_variable)
print("My global variable:", global_variable)
function(0)
Taková funkce se ale snadno může rozbÃt -- staÄà dotyÄnou globálnà promÄnnou smazat...
del global_variable
... a když funkci pÅÃÅ¡tÄ zavoláme, tak zkolabuje:
function(0)
Snad jeÅ¡tÄ horšà je to, že u takové funkce nenà možné jen na základÄ jejÃho volánà odhadnout, co pÅesnÄ udÄlá:
global_variable = 512
function(0)
global_variable = 1024
function(0)
V obou pÅÃpadech funkci voláme jako function(0), ale výsledek je pokaždé trochu jiný.
Mnohem lepšà je tedy psát funkce tak, aby použÃvaly jen lokálnà promÄnné a nebyly závislé na tÄch globálnÃch. Nejde to vždycky, ale vÄtÅ¡inu Äasu ano. MÃsto pÅÃmého odkazovánà na globálnà promÄnné je lepšà dát funkci vÃce parametrů, pÅes nÄž jà můžeme globálnà promÄnné zprostÅedkovanÄ pÅedávat, což je mnohem bezpeÄnÄjšà a pÅehlednÄjÅ¡Ã.
def function(parameter1, parameter2):
local_variable = 1
print("My first parameter:", parameter1)
print("My local variable:", local_variable)
print("My other parameter:", parameter2)
global_variable = 2
function(0, global_variable)
print vs. return¶
Jaký je rozdÃl v tom, když zavolám následujÃcà dvÄ funkce?
def funkce1():
print(1)
def funkce2():
return 1
funkce1()
funkce2()
V obou pÅÃpadech se mi zobrazà ÄÃslo 1, v druhém je navÃc vedle nÄj ÄÃslo v hranatých závorkách a dvojteÄka. V Äem se to liÅ¡Ã?
RozdÃl se lépe projevÃ, když se pokusÃme výsledek funkce uložit do promÄnné:
výsledek1 = funkce1()
výsledek1
výsledek2 = funkce2()
výsledek2
funkce1 ÄÃslo 1 jen v rámci svého bÄhu vytiskne na obrazovku, kdežto funkce2 ho vrátà jako svůj výsledek, takže ho můžeme uložit do promÄnné a dál s nÃm pracovat. Možná pomůže, když se podÃváme na funkci, která jedno ÄÃsle tiskne a jiné vracÃ:
def funkce3():
print(1)
return 2
výsledek3 = funkce3()
výsledek3
Zbývá tedy jen otázka: co vracà funkce1? Nic, když vůbec ani neobsahuje klÃÄové slovÃÄko return? Svým způsobem ano, ale i nic je v Pythonu nÄco ;) Což znà krypticky, ale je to jednoduché: Python má speciálnà objekt (konstantu) None, která reprezentuje "nic". Protože None je nic, tak se vám po vyhodnocenà ani neukáže:
None
A kdykoli nÄjaká funkce dobÄhne na konec svého receptu, aniž by potkala return, tak implicitnÄ automaticky vrátà jako svůj výsledek None.
VÃm, že je trochu záludné, že ono None nenà "vidÄt", ale v praxi si můžeme to, že funkce1 skuteÄnÄ vrátila None, lehce ovÄÅit:
výsledek1 is None
Pokročilé způsoby předávání argumentů¶
V Pythonu lze také vytvoÅit funkce, které pracujà s libovolným poÄtem poziÄnÃch nebo pojmenovaných argumentů. Sloužà k tomu speciálnà operátory * a **:
def flexibilnÃ_funkce(
poziÄnÃ_argument,
*zbytek_poziÄnÃch_argumentů,
pojmenovaný_argument,
**zbytek_pojmenovaných_argumentů
):
print("poziÄnà argument:", poziÄnÃ_argument)
print("zbytek poziÄnÃch argumentů:", zbytek_poziÄnÃch_argumentů)
print("pojmenovaný argument:", pojmenovaný_argument)
print("zbytek pojmenovaných argumentů:", zbytek_pojmenovaných_argumentů)
flexibilnÃ_funkce(1, 2, 3, 4, a=5, b=6, pojmenovaný_argument=7, c=8)
Parametr *zbytek_poziÄnÃch_argumentů sesbÃrá zbylé poziÄnà argumenty, sestavà z nich n-tici a tu uložà do promÄnné zbytek_poziÄnÃch_argumentů; podobnÄ parametr **zbytek_pojmenovaných_argumentů sesbÃrá zbylé pojmenované argumenty, sestavà z nich slovnÃk a uložà ho do promÄnné zbytek_pojmenovaných_argumentů (vÃc o n-ticÃch a slovnÃcÃch viz odd. Kolekce).
Jak vidno, můžeme tyto speciálnà parametry pojmenovat libovolnÄ, speciálnà chovánà jim propůjÄuje operátor *, resp. **. NicménÄ konvenÄnÄ se v Pythonu použÃvajà jména *args a **kwargs (jako keyword arguments).
A aby toho nebylo málo, funguje to i "naopak": máme-li seznam/n-tici (nebo slovnÃk) a chceme jeho položky pÅedat funkci jako poziÄnà (nebo pojmenované) argumenty, můžeme taky využÃt operátoru * (nebo **):
def nudná_funkce(a, b):
return a + b
poziÄnÃ_argumenty = [1, 2]
nudná_funkce(*poziÄnÃ_argumenty)
pojmenované_argumenty = {"a": 1, "b": 2}
nudná_funkce(**pojmenované_argumenty)
U poziÄnÃch argumentů musà pochopitelnÄ sedÄt poÄet...
poziÄnÃ_argumenty = [1, 2, 3]
nudná_funkce(*poziÄnÃ_argumenty)
... a u pojmenovaných jména:
pojmenované_argumenty = {"a": 1, "c": 2}
nudná_funkce(**pojmenované_argumenty)
Metody¶
Metody jsou funkce úzce spjaté s objekty. Seznam metod, které daný objekt podporuje, zÃskáme tak, že napÃÅ¡eme jméno promÄnné, která objekt obsahuje, za nÄ teÄku a stiskneme tabulátor:
obj.<TAB>
obj = "ahoj"
obj.upper()
FormálnÄ je metoda jen funkce "navÄÅ¡ená" na tÅÃdÄ:
class FooBar:
def get_my_type(self):
return type(self)
obj = FooBar()
obj.get_my_type()
Prvnà argument metody (self) odkazuje na instanci tÅÃdy, na které jsme metodu zavolali, tj. v pÅÃkladu výše na ten samý objekt, na který odkazuje promÄnná obj. Na rozdÃl od pÅÃpadných dalÅ¡Ãch argumentů se metodÄ pÅedává pomocà propojenà pÅes teÄku, ne v závorkách za metodou, nepÃÅ¡eme tedy obj.get_my_type(obj).
Čísla¶
V Pythonu jsou dva základnà typy ÄÃsel:
- celá ÄÃsla (angl. integer) -- typ
int - reálná ÄÃsla, reprezentovaná pomocà tzv. pohyblivé Åádové Äárky (angl. floating point) -- typ
float
Literály celých ÄÃsel vypadajà napÅ. takhle:
3
type(3)
-5
9543761
Pro lepšà Äitelnost mohou obsahovat i podtržÃtka:
9_543_761
Literály reálných ÄÃsel se vÄtÅ¡inou poznajà podle toho, že obsahujà desetinnou Äárku... tedy teÄku protože je to podle angliÄtiny:
3.14
type(3.14)
-2.72
0.1
# nulu můžeme vynechat
.1
OvÅ¡em ne nutnÄ -- Python má speciálnà syntax pro tzv. vÄdecký zápis ÄÃsel, a ÄÃsla zapsaná tÃmto způsobem jsou vždycky typu float, i když desetinnou teÄku neobsahujÃ.
# 1 à 10³
1e3
# 1 à 10â»Â³
1e-3
# 2.34 à 10²
2.34e2
KromÄ celých a reálných ÄÃsel disponuje Python i komplexnÃmi ÄÃsly:
3 + 4j
type(3 + 4j)
Jestli jste o nich nikdy neslyšeli, tak je zase pusťte z hlavy :)
KromÄ desÃtkové (decimálnÃ) soustavy, na kterou jsme vÅ¡ichni zvyklÃ, majà celá ÄÃsla v Pythonu literály i v soustavách jiných. Rozpoznáme je pomocà prefixů:
0bâ binárnà (dvojková) soustava -- použÃvá ÄÃslice 0 a 10oâ oktálnà (osmiÄková) soustava -- použÃvá ÄÃslice 0–70xâ hexadecimálnà (Å¡estnáctková) soustava -- použÃvá ÄÃslice 0–9 a a–f
0b11010
0o32
0x1a
Jak vidÃme, pÅevod na desÃtkovou soustavu je jednoduchý -- staÄà vyhodnotit literál v soustavÄ jiné a Python nám ho v odpovÄdi pÅevede do desÃtkové. K pÅevodu opaÄným smÄrem existujà funkce, které nám vrátà ÅetÄzec obsahujÃcà zápis ÄÃsla v požadované soustavÄ:
bin(26)
oct(26)
hex(26)
S ÄÃsly jdou provádÄt různé výpoÄty pomocà následujÃcÃch operátorů, které snad dÃky zkuÅ¡enosti s kalkulaÄkou budou působit pÅevážnÄ povÄdomÄ.
# sÄÃtánÃ
3 + 4
# odeÄÃtánÃ
3 - 4
# násobenÃ
3 * 4
# exponenciace
2**3
# dÄlenÃ
5 / 3
U dÄlenà si vÅ¡imnÄte, že výsledek je vždy float, i když dÄlÃme dva inty a výsledkem je nÄco, co my lidé chápeme jako celé ÄÃslo:
4 / 2
# celoÄÃselné dÄlenÃ
4 // 2
5 // 3
# modulo (= zbytek po celoÄÃselném dÄlenÃ)
5 % 3
Když chceme zároveÅ celoÄÃselné dÄlenà i zbytek po nÄm, můžeme použÃt zabudovanou funkci divmod.
divmod(5, 3)
divmod(5, 3) == (5 // 3, 5 % 3)
To se hodà napÅ. pÅi Äasových pÅevodech -- kolik je 143 vteÅin minut?
divmod(143, 60)
â 2 minuty a 23 vteÅin.
Zabudovaná funkce abs vrátà absolutnà hodnotu ÄÃsla:
abs(-4.1)
Pokud máme nÄjakou kolekci ÄÃsel (viz odd. Kolekce), můžeme identifikovat nejmenÅ¡Ã, resp. nejvÄtšà z nich pomocà zabudovaných funkcà min a max.
from math import inf
# inf reprezentuje nekoneÄno
inf
ÄÃsla = [0, -inf, 3.14, -2.72, inf]
min(ÄÃsla)
max(ÄÃsla)
Operátory majà různou prioritu, stejnÄ jako v matematice:
2**3 + 4 * 5
Pokud si nejsme poÅadÃm operacà jisti, můžeme ho pro jistotu specifikovat pomocà kulatých závorek:
(2**3) + (4 * 5)
Nebo pochopitelnÄ i zmÄnit:
2**(3 + 4) * 5
VÅ¡echny výše uvedené typy i zápisy ÄÃsel můžeme pÅi výpoÄtech libovolnÄ kombinovat, jen je potÅeba mÃt na pamÄti, že jakmile se nám jako dÃlÄà výsledek objevà float, i celkový výsledek bude float.
-0x1f + 165 * -0b1001 + 1.0
Ke konverzi z intu na float sloužà funkce float:
i = 3
float(i)
Dokáže též pÅevést textový zápis ÄÃsla (ÅetÄzec) na reálné ÄÃslo:
float("3.14")
float("3")
Analogicky funguje funkce int:
int(2.0)
int("2")
int(2.72)
VÅ¡imnÄte si, že funkce int prostÄ jen uÅÃzne desetinná mÃsta. Pokud chceme zaokrouhlit podle matematických zvyklostÃ, je potÅeba použÃt funkci round:
round(2.72)
PÅi poÄÃtánà s floaty je nutná jistá obezÅetnost. Kvůli tomu, jak jsou v pamÄti poÄÃtaÄe reprezentovány, nenà jejich zachycenà úplnÄ pÅesné, a nÄkteré výsledky tak můžou být pÅekvapivé...
.1 + .2
... což vede až k tomu, že nÄkteré oÄekávané rovnosti neplatÃ:
.1 + .2 == .3
PÅi porovnávánà floatů je bezpeÄnÄjšà mÃsto operátoru == (striktnà rovnosti) použÃvat funkci isclose z knihovny math (pÅibližná rovnost).
from math import isclose
isclose(.1 + .2, .3)
S ÄÃselnými promÄnnými Äasto narážÃme na následujÃcà situaci: vytvoÅÃme promÄnnou...
i = 0
i
... a následnÄ ji pak pomocà nÄjaké aritmetické operace potÅebujeme aktualizovat na základÄ staré hodnoty -- napÅ. pÅiÄÃst k původnÃmu ÄÃslu nÄjaké nové a výsledek znovu uložit do té samé promÄnné:
i = i + 2
i
Python umožÅuje zápis i = i + x zkrátit na i += x, abychom nemuseli psát i dvakrát:
i += 7
i
Tento zkrácený zápis funguje s libovolným operátorem.
i **= 2
i
Řetězce¶
ÅetÄzce (angl. strings, v Pythonu typ str) nám umožÅujà reprezentovat text jako uspoÅádanou sérii (ÅetÄzec) znaků. Literály ÅetÄzců majà různé varianty, vždy jsou ale ohraniÄené jednoduchými nebo dvojitými uvozovkami.
"hello"
'hello'
type("hello")
Chceme-li do ÅetÄzce uvozeného jednoduchými (dvojitými) uvozovkami vložit jednoduchou (dvojitou) uvozovku, je potÅeba zruÅ¡it jejà speciálnà význam "ukonÄovaÄ ÅetÄzce". K tomu sloužà zpÄtné lomÃtko. Angl. se ÅÃká the backslash escapes the next character.
"\"Hello,\" I said."
'\'Hello,\' I said.'
NÄkdy je ale jednoduššà prostÄ jen prostÅÃdat druh uvozovek, které sloužà k delimitaci ÅetÄzce, jak nám to ve svých odpovÄdÃch naznaÄuje sám Python.
ZpÄtné lomÃtko ale nesloužà jen k ruÅ¡enà speciálnÃho významu uvozovek -- jiným znakům speciálnà význam naopak dodává. Sloužà tak spÃÅ¡ jako pÅepÃnaÄ mezi doslovným a speciálnÃm významem. SpeciálnÃm sekvencÃm znaků, které tvoÅà zpÄtné lomÃtko + jeden Äi vÃce dalÅ¡Ãch znaků a majà jiný než doslovný význam, se angl. ÅÃká escape sequences.
NapÅ. sekvence \n se promÄnà ve znak nového Åádku, \t pak ve znak tabulátoru:
"a\t1\naa\t11"
Na prvnà pohled to tak nevypadá, ale to je jen kvůli tomu, že nás Python chce na tyto speciálnà znaky upozornit (což je dobÅe), takže když ÅetÄzec jen zobrazuje, reprezentuje tyto znaky pomocà dobÅe Äitelných speciálnÃch sekvencÃ. Aby se nové Åádky a tabulátory projevily, musÃme ÅetÄzec vytisknout:
print("a\t1\naa\t11")
Což odpovÃdá zamýšlené podobÄ, ale je mnohem tÄžšà poznat, co je v ÅetÄzci skuteÄnÄ za znaky. ÃplnÄ jiný ÅetÄzec může totiž vytiÅ¡tÄný vypadat zcela identicky:
print("a 1 \naa 11 ")
Speciálnà sekvence existujà též napÅ. pro vloženà libovolného unicodového znaku, ve formátu \uXXXX nebo \UXXXXXXXX, kde XXX... je hexadecimálnà zápis poÅadového ÄÃsla znaku v unicodové tabulce:
"asi nÄjaký ÄÃnský znak...? \u4e3e"
PoÅadové ÄÃslo znaku lze v Pythonu zÃskat pomocà funkce ord...
ord("Ä")
... takto zÃskáme jeho hexadecimálnà zápis...
hex(269)
... a ten pak můžeme použÃt ve speciálnà sekvenci:
"tohle by mÄlo být Ä: \u010d"
Inverznà funkcà k funkci ord je funkce chr -- dáme jà poÅadové ÄÃslo znaku a ona nám vrátà odpovÃdajÃcà znak.
chr(269)
chr(0x10d)
VÃce o znacÃch, Unicodu a kódovánà textu obecnÄ viz samostatný notebook unicode.ipynb.
Python pozná, když zpÄtné lomÃtko nepÅedcházà znaku Äi znakům, s nÃmž/nimiž by tvoÅilo speciálnà sekvenci, a vložà na takovém mÃstÄ doslovné zpÄtné lomÃtko, ale zároveÅ nám jemnÄ naznaÄÃ, že by byl radÅ¡i, kdybychom speciálnà pÅepÃnacà význam zpÄtného lomÃtka v literálu explicitnÄ vypnuli... pomocà zpÄtného lomÃtka:
"a \ b"
print("a \ b")
print("a \\ b")
"a \ b" == "a \\ b"
Z toho mj. plyne, že když chceme zpÄtných lomÃtek napsat vÃc za sebou, je jich potÅeba vždy dvojnásobek:
"a \\\\ b"
print("a \\\\ b")
Podobné literály zaÄnou brzy vypadat nepÅehlednÄ. ÄlovÄk pak zatoužà vÅ¡echny speciálnà sekvence zruÅ¡it, jen aby vÅ¡echny znaky fungovaly jednoduÅ¡e doslovnÄ. I to je možné, pomocà tzv. surových ÅetÄzců (angl. raw strings). V nich je zpÄtné lomÃtko jen dalšà znak:
r"\t \\\\ \n"
print(r"\t \\\\ \n")
Surové ÅetÄzce se velmi dobÅe hodà pro práci s regulárnÃmi výrazy (viz notebook regex.ipynb), kde se lomÃtka Äasto použÃvajà a hodà se nemuset pÅemýšlet o tom, jestl nám je Python nÄjak nepomÃchá.
Formátovacà ÅetÄzce (angl. format strings) umožÅujà pomocà složených závorek vkládat do ÅetÄzců libovolné výrazy:
x = 1
y = 2
f"{x} + {y} = {x + y}"
MÃsto pár jednoduchých/dvojitých uvozovek můžeme ÅetÄzce též delimitovat párem trojic jednoduchých/dvojitých uvozovek. Tyto literály můžou obsahovat doslovné znaky nového Åádku (ne jen speciálnà sekvenci \n) jednoduché/dvojité uvozovky bez lomÃtek (jen ne tÅi za sebou).
"""a
b
c"""
"""
a
b
c
"""
s = """"Hello," I said.
"Hi," she replied."""
print(s)
Jinak v nich platà stejná pravidla jako v bÄžných ÅetÄzcÃch, můžou být taktéž surové nebo formátovacà nebo obojà zároveÅ atp.
ÅetÄzce majà mnoho užiteÄných metod. NÄkolik jich zaÄÃná na is... a informujà nás o obsahu ÅetÄzce:
"hello".islower()
"HELLO".isupper()
"Hello".istitle()
"12".isnumeric()
Jiné vytvoÅà nový pozmÄnÄný ÅetÄzec:
"hello".upper()
"hello".title()
Metoda strip oseká z okrajů ÅetÄzce prázdné znaky (angl. whitespace)...
" \n ZZZ\n \t \n".strip()
... popÅÃpadÄ libovolné znaky, které jà zadáme:
"bbabZZZaabaaa".strip("ab")
Metoda split naseká ÅetÄzec na dÃlÄà ÅetÄzce na prázdných znacÃch...
"a b c".split()
... nebo na každém výskytu zadaného podÅetÄzce:
"a, b, c".split(", ")
Prázdných znaků může být libovolné množstvà a pokud dÄlenÃm vznikne prázdný ÅetÄzec, tak je z výsledku odebrán...
" a\n b \t c\n ".split()
... ale pokud zadáme podÅetÄzec, hledá metoda doslova a do pÃsmene pÅesnÄ jeho výskyty a pÅÃpadné prázdné ÅetÄzce ve výstupu zachovává:
" a, b, , c".split(", ")
Opakem metody split je metoda join, která pospojuje kolekci ÅetÄzců, kterou jà pÅedáme jako argument:
"-".join(["a", "b", "c"])
# i ÅetÄzec lze chápat jako kolekci jednoznakových ÅetÄzců
"-".join("abc")
Důležité je, že ÅetÄzce patÅà k tzv. nemodifikovatelným typům, tj. aÅ¥ s nimi dÄláme co dÄláme...
s = " a,b,c "
s
s.strip().split(",")
... původnà ÅetÄzec vždy zůstane nedotÄený:
s
VÃce o (ne)modifikovatelných typech viz odd. Kolekce.
JeÅ¡tÄ pár slov k funkci print. Tato funkce vytiskne vÅ¡echny svoje poziÄnà argumenty oddÄlené mezerami a na závÄr pÅilepà znak nového Åádku.
print(1, "a", [])
OddÄlovaÄ (mezeru) i ukonÄovaÄ (nový Åádek) si můžeme upravit pomocà pojmenovaných argumentů sep a end -- může to být libovolný jiný ÅetÄzec.
print(1, "a", [], sep="__ODDÄLOVAÄ__", end="__UKONÄOVAÄ__")
Jak to, že print umà vytisknout i jiné objekty než jen ÅetÄzce? Ve skuteÄnosti neumÃ, jen na každý poziÄnà argument zavolá funkci str, která ho právÄ na ÅetÄzec pÅevede.
str(1)
str([])
U objektů, které již ÅetÄzci jsou, žádný pÅevod pochopitelnÄ nenà potÅeba, str jako výsledek vrátà nezmÄnÄný argument:
str("a")
Kolekce¶
Kolekce je objekt, který obsahuje dalšà objekty, které z nÄj lze jeden po druhém vytáhnout. Mezi základnà zabudované typy kolekcà patÅÃ:
- seznamy -- typ
list - n-tice -- typ
tuple - množiny -- typ
set - slovnÃky -- typ
dict
Svým způsobem můžeme i na ÅetÄzce nahlÞet jako na kolekce znaků -- jak uvidÃme nÞe, jde s nimi provádÄt stejné typy operacÃ. StejnÄ jako ostatnà kolekce napÅ. reagujà na funkci len, která vracà poÄet prvků v kolekci...
len("abc")
... nebo na operátor in, který testuje pÅÃtomnost prvku v kolekci...
"b" in "abc"
... nebo na funkci sorted, která umà na základÄ kolekce vyrobit seÅazený seznam jejÃch prvků:
sorted("bca")
Seznamy¶
Kl̀ov̩ vlastnosti:
- uspoÅádanost (angl. se uspoÅádaná kolekce Åekne ordered collection): zachovává poÅadà prvků
- prvky se mohou opakovat
- modifikovatelnost (angl. mutability): seznam lze kdykoli upravovat (pÅidávat/ubÃrat prvky)
Literál seznamu sestává z hranatých závorek, mezi nÄž vypÃÅ¡eme poÄáteÄnà prvky seznamu oddÄlené Äárkami:
[1, "a"]
# opakované prvky
[1, 1, 1]
# prázdný seznam
[]
KromÄ literálu můžeme seznam vytvoÅit jeÅ¡tÄ pomocà funkce list, napÅ. promÄnit ÅetÄzec na seznam znaků:
list("abc")
Argument pro funkci list může být jakýkoli iterovatelný objekt, tj. objekt, ze kterého lze opakovanÄ tahat dalšà objekty jako králÃky z klobouku (iterace = opakovánÃ). VÄtÅ¡inou se jedná o různé typy kolekcÃ, ale ne nutnÄ. NapÅ. funkce range umà vytvoÅit objekt, z nÄjž lze postupnÄ tahat ÄÃsla v zadaném rozpÄtÃ:
list(range(3))
# prázdný seznam
list()
VytvoÅme si nynà seznam na hranÃ:
zvÃÅata = "koÄka pes morÄe slon žirafa koÄka".split()
zvÃÅata
DÃky tomu, že je seznam uspoÅádaný, má každý jeho prvek poÅadového ÄÃslo, tzv. index (ÄÃslováno od nuly). Pomocà indexů lze k jednotlivým prvkům seznamu pÅistupovat ("ukázat" si na nÄ -- index pocházà z latinského slova pro ukazováÄek). Této operaci se ÅÃká indexace a vypadá tak, že za seznam napÃÅ¡eme hranaté závorky s požadovaným indexem:
zvÃÅata[0]
Záporná ÄÃsla indexujà odzadu:
zvÃÅata[-1]
Teoreticky lze indexaci pÅilepit pÅÃmo za literál seznamu, byÅ¥ to v praxi nenà moc užiteÄné a zápis vypadá zvláštnÄ:
["koÄka", "pes", "morÄe"][2]
Dohledat index nÄjakého prvku můžeme pomocà metody index:
zvÃÅata.index("morÄe")
zvÃÅata.index("orangutan")
SpoÄÃtat poÄet výskytů nÄjakého prvku můžeme pomocà metody count:
zvÃÅata.count("koÄka")
Když chceme vytáhnout ze seznamu ne jeden prvek, ale podseznam, použijeme pÅi indexaci tzv. výÅez (angl. slice):
zvÃÅata[2:4]
Prvnà ÄÃslo je index prvku, jÃmž podseznam zaÄÃná, druhé ÄÃslo je index prvnÃho prvku, který již do podseznamu nepatÅÃ. To je na prvnà pohled možná trochu zvláštnÃ, ale v kombinaci s tÃm, že vypuÅ¡tÄnÃm prvnÃho/druhého ÄÃsla dosáhneme toho, že výÅez bude od zaÄátku/do konce, nám to umožÅuje jednoduÅ¡e rozÅÃznout seznam na nepÅekrývajÃcà se Äásti:
hranice = 3
zvÃÅata[:hranice]
zvÃÅata[hranice:]
Když vynecháme obÄ ÄÃsla, vytvoÅÃme podseznam odpovÃdajÃcà původnÃmu seznamu:
zvÃÅata[:]
# což je to samé jako toto
zvÃÅata[0:len(zvÃÅata)]
Ve výÅezu můžeme volitelnÄ specifikovat jeÅ¡tÄ tÅetà ÄÃslo, které stanovuje, že do seznamu má být zahrnutý jen každý x-tý prvek (napÅ. každý druhý):
zvÃÅata[1:4:2]
Když je toto ÄÃslo záporné, můžeme podseznam vyÅÃznou v opaÄném smÄru (zprava doleva):
zvÃÅata[4:1:-2]
# takto lze tedy pÅevrátit poÅadà seznamu
zvÃÅata[::-1]
# ale srozumitelnÄjšà je asi tento ekvivalentnà zápis
# pomocà funkce reversed
list(reversed(zvÃÅata))
KromÄ vyÅezávánà lze nové seznamy vytváÅet i spojovánÃm seznamů pomocà operátoru +...
zvÃÅata + ["mastodont"]
... nebo klonovánÃm pomocà operátoru *:
2 * zvÃÅata
ÅÃkali jsme si, že seznamy jsou modifikovatelné, že lze prvky libovolnÄ ubÃrat a pÅidávat. NicménÄ vÅ¡echno, co jsme zatÃm s naÅ¡Ãm seznamem zvÃÅata provádÄli, na nÄm nezkÅivilo ani vlásek:
zvÃÅata
ZatÃm jsme tedy tento seznam nijak nemodifikovali, vÅ¡echny operace, které jsme na nÄj aplikovali, vedly k tomu, že na jeho základÄ vznikl nový, který z toho původnÃho nÄjakým způsobem vycházel.
K modifikaci seznamu sloužà nÄkteré jeho metody. Ukažme si to na pÅÃkladu seÅazovánÃ. Samostatná funkce sorted vytvoÅà na základÄ původnÃho seznamu nový seznam, který obsahuje stejné prvky, ovÅ¡em seÅazené, a vrátà ho jako výsledek:
seÅazená_zvÃÅata = sorted(zvÃÅata)
seÅazená_zvÃÅata
Můžeme si jednoduÅ¡e ovÄÅit, že zvÃÅata a seÅazená_zvÃÅata jsou dva různé objekty...
zvÃÅata is seÅazená_zvÃÅata
... a ani si nejsou rovné (protože záležà na poÅadÃ):
zvÃÅata == seÅazená_zvÃÅata
Naopak metoda sort seÅadà původnà seznam, na kterém ji zavoláme, a vrátà None:
# v tuto chvÃli poprvé modifikujeme seznam zvÃÅata...
zvÃÅata.sort()
# ... což si můžeme jednoduÅ¡e ovÄÅit (zmÄnilo se poÅadÃ)
zvÃÅata
Seznamy zvÃÅata a seÅazená_zvÃÅata jsou nadále různé objekty...
zvÃÅata is seÅazená_zvÃÅata
... ale v tuto chvÃli už jsou si rovné (protože teÄ už obsahujà stejné prvky ve stejném poÅadÃ):
zvÃÅata == seÅazená_zvÃÅata
NÄkteré dalšà užiteÄné modifikujÃcà metody objektů typu list:
# pÅidánà jednoho prvku na konec
zvÃÅata.append("užovka")
zvÃÅata
# pomocà indexace lze nahradit jeden prvek...
zvÃÅata[4] = "slonice"
zvÃÅata
# ... nebo celý podseznam:
zvÃÅata[4:6] = ["slon", "žirafáÄ"]
zvÃÅata
# pÅidánà jednoho prvku na libovolný index
zvÃÅata.insert(1, "kocour")
zvÃÅata
# pÅidánà celé kolekce prvků na konec
zvÃÅata.extend(["kapr", "Å¡tika", "candát"])
zvÃÅata
# odebránà poslednÃho prvku
zvÃÅata.pop()
zvÃÅata
# odebránà prvku na libovolném indexu
zvÃÅata.pop(0)
zvÃÅata
# odebránà prvnÃho výskytu konkrétnÃho prvku
zvÃÅata.remove("pes")
zvÃÅata
# pÅevrácenà poÅadà prvků
zvÃÅata.reverse()
zvÃÅata
Důležitým důsledkem modifikovatelnosti je, že pokud na ten samý seznam odkazuje vÃce promÄnných (= má vÃce jmen), jakákoli modifikace se projevà pod vÅ¡emi jmény. PÅedstavte si analogii: pokud mám bratra, který se jmenuje Jan, tak když se Jan nechá ostÅÃhat, bude ostÅÃhaný i můj bratr, protože je to jedna a tatáž osoba. PodobnÄ se seznamy:
jan = ["nohy", "trup", "hlava", "vlasy"]
bratr = jan
jan is bratr
# teÄ Jana "ostÅÃháme"
jan.pop()
jan
bratr
Modifikace je oÅ¡emetná zejména v pÅÃpadÄ, kdy je skrytá v nÄjaké funkci a týká se argumentů, které funkci pÅedáváme zvenÄÃ. Pak nám vůbec nemusà dojÃt, že argumenty vlastnÄ modifikujeme:
def zkrášlit(osoba):
# osobu zkrášlÃme tak, že ji ostÅÃháme
osoba.pop()
josef = ["nohy", "trup", "hlava", "vlasy"]
zkrášlit(josef)
Nic nenà vidÄt, zafungovala to vůbec, podaÅilo se nám Josefa zkrášlit? No pro jistotu to zkusÃme jeÅ¡tÄ jednou, tÃm pÅece nemůžeme nic zkazit, že...
zkrášlit(josef)
Tak co teÄ? PodÃvejme se na Josefa...
josef
Ježišmarja chudák Josef, kde má hlavu?!
(Je to trochu fórek pórek, ale snad si rozumÃme, v Äem tkvà nebezpeÄà :) )
ZávÄrem -- s modifikacà je potÅeba být opatrný a radÄji s nà šetÅit:
- pokud možno pÅÃmo nemodifikovat argumenty funkcÃ
- kde se to hodÃ, použÃvat mÃsto seznamů nemodifikovatelné n-tice
n-tice¶
KlÃÄové vlastnosti jsou stejné jako u seznamů jen s tÃm rozdÃlem, že jsou nemodifikovatelné. Lze s nimi tedy provádÄt ty samé operace, s výjimkou tÄch modifikujÃcÃch.
Literály n-tic v zásadÄ mohou sestávat jen z prvků oddÄlených Äárkami:
1, "a"
NicménÄ v praxi je Äasto potÅeba obalit n-tici do kulatých závorek, protože Äárka má jako operátor velmi nÃzkou prioritu (viz odd. ÄÃsla). Je to podobné, jako když musÃme použÃt závorky, aby sÄÃtánà probÄhlo pÅed dÄlenÃm -- napÅ. 2 + 3 à 4 = 14 vs. (2 + 3) à 4 = 20. Kdo se tÃm nechce moc zabývat, může závorky okolo n-tic použÃvat poÅád.
(1, "a")
Specifickou syntax má prázdná n-tice...
()
type(())
len(())
... a jednoprvková n-tice (Äárka pÅed uzavÃracà závorkou je povinná):
("a",)
Jiný iterovatelný objekt na n-tici promÄnÃme pomocà funkce tuple:
tuple([1, "a"])
tuple("abc")
tuple(range(3))
JednÃm z důsledků nemodifikovatelnosti n-tic je, že do nich lze sice indexovat...
ntice = tuple("abc")
ntice[1]
... ale už nelze pomocà indexace prvky nahrazovat jinými:
ntice[1] = "Z"
PodobnÄ ÅetÄzce, které jsou také nemodifikovatelné:
ÅetÄzec = "abc"
ÅetÄzec[1]
ÅetÄzec[1] = "Z"
Množiny¶
Kl̀ov̩ vlastnosti:
- neuspoÅádanost: prvky nemajà dané poÅadÃ
- prvky se nesmà opakovat
- modifikovatelnost
Literály vypadajà následovnÄ:
{1, "a"}
OvÅ¡em pozor, {} nenà prázdná množina, ale prázdný slovnÃk (viz nÞe)! Prázdnou množinu zÃskáme pomocà funkce set...
set()
... která nám posloužà i k pÅevodu jiné kolekce na množinu:
set("koÄiÄka")
Prvky v množinÄ se nesmÄjà opakovat v tom smyslu, že do množiny nelze vložit dva prvky a a b, o nichž platÃ, že a == b. NicménÄ nenà tÅeba se bát, když se o to pokusÃme, nenastane chyba, množina prostÄ druhý pokus jednoduÅ¡e ignoruje:
{1, 2, 1}
set([1, 2, 1])
VytvoÅme si dvÄ množiny na hranÃ:
set1 = {1, 2, 3}
set2 = {2, 3, 4}
Důležitým důsledkem neuspoÅádanosti je, že do množiny nelze indexovat -- ptát se po "prvnÃm" prvku množiny nedává smysl:
set1[0]
Množiny podporujà různé množinové operace. NÄkteré jsou nemodifikujÃcÃ...
# sjednocenÃ
set1.union(set2)
# též možno pomocà operátoru |
set1 | set2
# průnik
set1.intersection(set2)
# též možno pomocà operátoru &
set1 & set2
# rozdÃl
set1.difference(set2)
# též možno pomocà operátoru -
set1 - set2
# vztahy mezi množinami
set1.issubset(set2)
... jiné jsou modifikujÃcÃ:
# pÅidánà prvku
set1.add(4)
set1
# zde k modifikaci nedojde, protože set1 už prvek 4 obsahuje
set1.add(4)
set1
# odebránà prvku
set1.remove(4)
set1
# pÅidánà vÃce prvků, ovÅ¡em znovu pochopitelnÄ s vyÅazenÃm duplicit
set1.update([0, 1, 2])
set1
# průnik, pÅiÄemž výsledek operace se uložà do původnà množiny set1
set1.intersection_update(set2)
set1
TeÄ už tedy platÃ:
set1.issubset(set2)
# též možno pomocà operátoru < ("menšà než")
set1 < set2
set2.issuperset(set1)
# též možno pomocà operátoru > ("vÄtšà než")
set2 > set1
KromÄ toho, že nám množiny dobÅe posloužÃ, když chceme kolekci s duplicitami zredukovat na kolekci unikátnÃch objektů (napÅ. když chceme seznam tokenů v textu pÅevést na množinu typů), majà jeÅ¡tÄ jednu výhodu: dÃky tomu, že prvky nemusà respektovat poÅadà zadané uživatelem, můžou být internÄ uspoÅádané tak, aby umožÅovaly velmi rychle zjistit, zda množina daný prvek obsahuje Äi ne.
V pÅÃpadÄ seznamu je potÅeba ho procházet prvek po prvku, od zaÄátku do konce, abychom zjistili, zda daný prvek obsahuje Äi ne. ÄÃm je seznam delÅ¡Ã, a ÄÃm dál v nÄm prvek je (nebo pokud v nÄm prvek vůbec nenÃ), tÃm déle to trvá:
# seznam prvnÃho milionu ÄÃsel
seznam = list(range(1_000_000))
# Python musà seznam projÃt celý, prvek po prvku, aby zjistil,
# že ÅetÄzec "a" v naÅ¡em seznamu prvnÃho milionu ÄÃsel nenÃ
"a" in seznam
%%timeit
# speciálnà direktiva %%timeit nenà pÅÃmo souÄástà Pythonu,
# poskytuje ji prostÅedà Jupyter; sloužà k tomu, že spustÃ
# danou buÅku opakovanÄ, pokaždé zmÄÅÃ, jak dlouho trvá
# buÅku vykonat, a pak nám ukáže průmÄrný Äas
"a" in seznam
Oproti tomu v množinÄ lze dÃky jejÃmu internÃmu uspoÅádánà dohledat prvek velmi rychle:
množina = set(seznam)
"a" in množina
%%timeit
"a" in množina
U seznamu trvá naÅ¡e operace v Åádu desÃtek milisekund (10â»Â³ s), kdežto u množiny jsou to desÃtky nanosekund (10â»â¹ s) -- je tedy milionkrát rychlejÅ¡Ã!
Nemodifikovatelným protÄjÅ¡kem typu set je typ frozenset.
Slovníky¶
SlovnÃky se od ostatnÃch kolekcÃ, které jsme doposud potkali, lišà v tom, že obsahujà dva typy prvků:
- klÃÄe (angl. keys), podle nichž se ve slovnÃku hledá
- a hodnoty (angl. values), které pod klÃÄi dohledáme
Je to podobnÄ jako s jazykovými slovnÃky (proto se tak v Pythonu jmenujÃ): když ve slovnÃku hledám slovo "koÄka", tak "koÄka" je klÃÄ, a definice, kterou najdu, je jeho odpovÃdajÃcà hodnota.
StÄžejnà vlastnosti slovnÃků:
- neuspoÅádanost: prvky nemajà dané poÅadÃ
- klÃÄe se nesmà opakovat, ale hodnoty klidnÄ můžou
- modifikovatelnost
Literály vypadajà následovnÄ:
{"a": 1, "b": 2}
Prvek pÅed dvojteÄkou je vždy klÃÄ, za dvojteÄkou pak jeho odpovÃdajÃcà hodnota.
Když stejný klÃÄ specifikujeme vÃckrát, nenastane chyba, jen poslednà asociovaná hodnota pÅemaže ty pÅedchozÃ:
{"a": 1, "a": 2, "a": 3}
# prázdný slovnÃk
{}
type({})
SlovnÃky lze vytváÅet i pomocà funkce dict, a to v zásadÄ dvÄma způsoby. Prvnà možnost je, že do slovnÃku "nasypeme" kolekci sestávajÃcà z dvojic objektů, z nichž prvnà vždy bude brán jako klÃÄ a druhý jako jeho asociovaná hodnota:
# napÅ. seznam n-tic o dvou položkách...
dict([("a", 1), ("b", 2)])
# ... nebo klidnÄ seznam seznamů o dvou položkách...
dict([["a", 1], ["b", 2]])
# ... nebo tÅeba n-tice ÅetÄzců o dvou znacÃch atp.
dict(("ab", "cd"))
Druhá možnost je, že využijeme pojmenované argumenty -- jména argumentů pak skonÄà jako klÃÄe, argumenty samotné jako hodnoty:
dict(a=1, b=2)
VytvoÅme si slovnÃk na hranÃ:
slovnÃk = {
"pes": "nejlepšà pÅÃtel ÄlovÄka",
"koÄka": "vypoÄÃtavá potvora"
}
SlovnÃky jsou sice stejnÄ jako množiny neuspoÅádané, ale indexaci na rozdÃl od nich podporujÃ. Jen se jako index nepoužÃvá poÅadové ÄÃslo (protože žádné poÅadà neexistuje), ale klÃÄ:
slovnÃk["koÄka"]
PÅes metodu keys se dostaneme ke vÅ¡em klÃÄům ve slovnÃku...
slovnÃk.keys()
... pÅes metodu values k hodnotám...
slovnÃk.values()
... a pÅes metodu items k uspoÅádaným dvojicÃm (klÃÄ, hodnota):
slovnÃk.items()
Když se pokusÃme indexovat podle klÃÄe, který ve slovnÃku nenÃ, Python zkolabuje:
slovnÃk["morÄe"]
Abychom se kolapsu (a tÃm i zastavenà bÄhu programu) vyhnuli, můžeme použÃt metodu get, která v pÅÃpadÄ absence klÃÄe vrátà mÃsto hodnoty ve slovnÃku nÄjaký jiný objekt (defaultnÄ je to None, ale můžeme specifikovat libovolný objekt).
slovnÃk.get("pes")
slovnÃk.get("morÄe")
slovnÃk.get("morÄe", "MORÄE VE SLOVNÃKU NENÃ!")
DÃky tomu, že jsou slovnÃky modifikovatelné, můžeme hodnoty odpovÃdajÃcà jednotlivým klÃÄům libovolnÄ mÄnit...
slovnÃk["koÄka"] = "chlupatý mazlÃÄek"
slovnÃk
... nebo pÅidávat nové:
slovnÃk["rybiÄka"] = "nÄmá slizká tváÅ"
slovnÃk
V prvnÃm pÅÃpadÄ jsme pÅepsali původnà hodnotu odpovÃdajÃcà klÃÄi "koÄka", ve druhém jsme pÅidali nový klÃÄ "rybiÄka" a k nÄmu odpovÃdajÃcà hodnotu. Syntax (zápis) ale vypadá v obou pÅÃpadech stejnÄ.
ObÄas potÅebujeme zajistit, aby za žádnou cenu nedoÅ¡lo k pÅepsánà existujÃcà hodnoty (viz prvnà pÅÃpad výše). K tomu posloužà metoda setdefault -- pokud klÃÄ už ve slovnÃku existuje, původnà hodnotu nezmÄnÃ, jen ji vrátÃ...
slovnÃk.setdefault("koÄka", "tuhle novou hodnotu do slovnÃku neprocpu :(")
slovnÃk
... ale pokud neexistuje, klÃÄ a odpovÃdajÃcà hodnotu do slovnÃku pÅidá (a hodnotu taky vrátÃ):
slovnÃk.setdefault("morÄe", "ale tuhle procpu :)")
slovnÃk
Metoda setdefault tedy funguje podobnÄ jako metoda get s tÃm rozdÃlem, že v pÅÃpadÄ nepÅÃtomnosti klÃÄe ve slovnÃku defaultnà hodnotu nejen vrátÃ, ale i uložà do slovnÃku.
Metoda update, která pÅidává nové prvky do slovnÃku / upravuje stávajÃcÃ, nabÃzà co do argumentů, které zvládne zpracovat, podobné možnosti jako samotná funkce dict:
# libovolná kolekce dvojic prvků...
slovnÃk.update([("a", 1), ("b", 2)])
slovnÃk
slovnÃk.update(("ab", "cd"))
slovnÃk
# ... jiný slovnÃk...
slovnÃk.update({"b": "z"})
slovnÃk
# ... nebo pojmenované argumenty:
slovnÃk.update(žralok="dravá moÅská paryba", kůÅ="tam nÄkde v pastvinách")
slovnÃk
OdebÃrat prvky ze slovnÃku lze různými způsoby, podle toho, zda chceme odebrat konkrétnà klÃÄ a chceme jeÅ¡tÄ pracovat s jeho hodnotou...
# metoda pop smaže prvek podle klÃÄe a vrátà odpovÃdajÃcà hodnotu
slovnÃk.pop("a")
slovnÃk
... nebo zda hodnotu k niÄemu nepotÅebujeme...
# operátor del smaže prvek podle klÃÄe a nevrátà nic
del slovnÃk["b"]
slovnÃk
... nebo zda je nám jedno, který prvek odebereme (necháme Python vybrat za nás):
# metoda popitem odebere nÄjaký prvek (nevÃme pÅedem jaký)
# a vrátà klÃÄ a hodnotu jako n-tici
slovnÃk.popitem()
slovnÃk
DÃky neuspoÅádanosti a zákazu opakovánà je dohledávánà klÃÄů ve slovnÃku velmi rychlé (stejnÄ jako u množin) a zapisuje se velmi jednoduÅ¡e:
"koÄka" in slovnÃk
Hodnoty ale ve slovnÃku takhle jednoduÅ¡e nedohledáme:
"nejlepšà pÅÃtel ÄlovÄka" in slovnÃk
Je potÅeba je projÃt jednu po druhé pomocà metody values, což na rozdÃl od klÃÄů trvá stejnÄ dlouho jako u seznamu (asi jako kdybychom v jazykovém slovnÃku hledali tak, že bychom proÄÃtali jen definice):
"nejlepšà pÅÃtel ÄlovÄka" in slovnÃk.values()
Můžeme si to ovÄÅit na libovolném velkém slovnÃku. K vytvoÅenà takového velkého testovacÃho slovnÃku můžeme použÃt zabudovanou funkci zip, která "sezipuje" dohromady prvky nÄkolika iterovatelných objektů. Znà to složitÄ, ale když se na ni podÃváme v akci, je to myslÃm jednoduché:
# vÅ¡e obalÃme do funkce list, aby se výsledné "sezipované" prvky
# uložily do seznamu
list(zip("abc", [1, 2, 3]))
Pomocà funkce zip jednoduÅ¡e můžeme vytvoÅit slovnÃk, v nÄmž namapujeme prvnà milion ÄÃsel na sebe sama, ve stylu...
dict(zip(range(3), range(3)))
... akorát vÄtÅ¡Ã. Takový slovnÃk samozÅejmÄ nenà k niÄemu užiteÄný, jen je velký, takže dobÅe posloužà k ilustraci rozdÃlu rychlosti v hledánà mezi klÃÄi a hodnotami.
libovolný_velký_slovnÃk = dict(zip(range(1_000_000), range(1_000_000)))
Hledánà v hodnotách:
"a" in libovolný_velký_slovnÃk.values()
%%timeit
"a" in libovolný_velký_slovnÃk.values()
Hledánà v klÃÄÃch:
"a" in libovolný_velký_slovnÃk
%%timeit
"a" in libovolný_velký_slovnÃk
Znovu pozorujeme rozdÃl Å¡esti Åádů: hledánà v klÃÄÃch je milionkrát rychlejšà než hledánà v hodnotách.
A když už máme slovnÃku plné zuby, můžeme jeho obsah vymazat a zaÄÃt nanovo:
libovolný_velký_slovnÃk.clear()
libovolný_velký_slovnÃk
Vnořené kolekce¶
Jak už jsme ostatnÄ v nÄkterých pÅÃkladech výše naznaÄili, kolekce lze do sebe libovolnÄ zanoÅovat, ÄÃmž můžeme reprezentovat různé složité strukturnà vztahy. Literálnà zápis vnoÅených kolekcà je celkem intuitivnÃ:
slovnÃk = {
"hesla": {
"koÄka": {
"definice": "chlupatý mazlÃÄek",
"pÅÃklad": "Na oknÄ sedÄla koÄka...",
},
"pes": {
"definice": "nejlepšà pÅÃtel ÄlovÄka",
"pÅÃklad": "... a venku Å¡tÄkal pes.",
},
},
"autoÅi": [
{"jméno": "John", "pÅÃjmenÃ": "Doe"},
{"jméno": "Jane", "pÅÃjmenÃ": "Doe"},
],
"datum": (2018, 11, 7),
}
Naproti tomu indexace do vnoÅených kolekcà obÄas lidem Äinà pÅi prvnÃm setkánà potÞe. Chceme-li napÅ. vytáhnout ze slovnÃku definici koÄky, nÄkdo má tendenci zkouÅ¡et následujÃcà zápis, který svým vnoÅenÃm zrcadlà vnoÅenà literálu:
slovnÃk["hesla"["koÄka"]]
Chybová hláška znà trochu krypticky, ale můžeme z nà odvodit, že se Python zÅejmÄ snažà indexovat do nÄjakého ÅetÄzce (tj. vytáhnout znak z ÅetÄzce), což jsme rozhodnÄ nezamýšleli, takže zápis musà být Å¡patnÄ.
Co se tedy dÄje? PojÄme si výraz rozebrat tak, jak ho vidà Python. NejdÅÃv vidà promÄnnou slovnÃk, za nà hranaté závorky. Usoudà tedy (správnÄ), že se snažÃme pÅistoupit k nÄjakému klÃÄi ve slovnÃku. K jakému klÃÄi? To je potÅeba zjistit na základÄ obsahu hranatých závorek. Jinými slovy, Python v tuto chvÃli vidà slovnÃk[X], kde X je zástupný znak pro klÃÄ, který chceme, aby ve slovnÃku dohledal.
DobÅe, tak dál. Abych mohl X dohledat, uvažuje Python, musÃm nejdÅÃv zjistit, co je zaÄ. Hm, podle vÅ¡eho mám za X dosadit výraz "hesla"[Y]. Ten musÃm tedy vyhodnotit jako prvnÃ, abych mohl posléze vyhodnotit výraz slovnÃk[X].
Co je "hesla"[Y] za výraz? Je to výraz, v nÄmž máme ÅetÄzec "hesla", a pomocà indexace se z nÄj snažÃme vytáhnout znak, který odpovÃdá indexu Y.
SkvÄle, tetelà se Python, už tedy staÄà jen zjistit, jaká je hodnota indexu Y, a můžu zaÄÃt celý výraz vyhodnocovat, pÄknÄ zvnitÅka k vnÄjÅ¡ku. Jenže ouha, Y by mÄlo být poÅadové ÄÃslo požadovaného znaku, ale mÃsto toho je to ÅetÄzec "koÄka". Chudák Python nevÃ, co to znamená, vytáhnout "koÄka"-tý znak z ÅetÄzce "hesla", a tak to vzdá a jen si postÄžuje, že indexy do ÅetÄzců by mÄly být celá ÄÃsla ("string indices must be integers").
Jinými slovy, vyhodnocovánà celého výrazu selže na tomto podvýrazu:
"hesla"["koÄka"]
Jak vidno, chyba je stejná.
Jak tedy na to? Vždycky je možné rozdÄlit indexaci do vnoÅené kolekce na vÃc jednoduchých indexacà s použitÃm pomocných promÄnných:
hesla = slovnÃk["hesla"]
koÄka = hesla["koÄka"]
koÄka
Z výše uvedeného zápisu ale plyne, že se mezipromÄnné hesla můžeme zbavit pomocà dosazovánÃ. ZaÄnÄme od promÄnné koÄka, která obsahuje zamýšlený výsledek (slovnÃkové heslo pro koÄku; mezery sloužà jen ke zvýraznÄnà toho, jak probÃhá dosazovánÃ):
koÄka
Za výraz koÄka lze dosadit výraz hesla["koÄka"] (protože koÄka = hesla["koÄka"]):
hesla ["koÄka"]
VýbornÄ, výsledek zůstal stejný, takže dosazenà platÃ. Dále můžeme za výraz hesla dosadit výraz slovnÃk["hesla"] (protože hesla = slovnÃk["hesla]), pÅiÄemž útržek kódu ["koÄka"] zůstane tam, kde byl:
slovnÃk["hesla"] ["koÄka"]
SkvÄle, výsledek je stále stejný! TeÄ už se jen zbavÃme mezer, aby byl zápis konvenÄnÄjÅ¡Ã, a vymysleli jsme způsob, jak indexovat do vnoÅených kolekcÃ.
slovnÃk["hesla"]["koÄka"]
# rok
slovnÃk["datum"][0]
# pÅÃkladová vÄta pro psa
slovnÃk["hesla"]["pes"]["pÅÃklad"]
# jméno druhého autora
slovnÃk["autoÅi"][1]["jméno"]
A tak podobnÄ.
Nezabudované kolekce¶
Existuje samozÅejmÄ mnoho nezabudovaných kolekcà -- napÅ. tÅÃda FreqDist z knihovny nltk je taky kolekce (obsahuje dÃlÄà prvky). Nezabudované kolekce pochopitelnÄ nemajà literály, je potÅeba je inicializovat pomocà pÅÃsluÅ¡ného konstruktoru tÅÃdy (viz odd. Typy a tÅÃdy):
from nltk import FreqDist
FreqDist("aababc")
Opakování operací: for-cyklus¶
Äasto chceme nÄjakou operaci provést opakovanÄ, ale pokaždé s trochu jinými daty. NapÅ. chceme vytisknout ÄÃsla od 0 do 2. Mohli bychom samozÅejmÄ použÃt copy-paste, tj. Åádek tÅikrát zkopÃrovat a pokaždé jen nahradit ÄÃslo...
print(0)
print(1)
print(2)
... jenže pak je problém, že jakmile chceme akci trochu upravit, tÅeba vytisknout "ÄÃslo: 0" mÃsto jen "0", a podobnÄ pro ostatnà ÄÃsla, musÃme provést úpravu na každém Åádku:
print("ÄÃslo:", 0)
print("ÄÃslo:", 1)
print("ÄÃsloo:", 2)
PÅi takovém opisovánà / kopÃrovánà je snadné udÄlat na jednom mÃstÄ chybu (viz výše "ÄÃsloo" mÃsto "ÄÃslo") a najednou mÃsto toho, abychom provedli tÅikrát tu samou operaci, provedeme dvakrát jednu a potÅetà trochu jinou.
Proto je lepšà se zamyslet, co se pÅi každém opakovánà mÄnà a co naopak zůstává stejné, a zaznamenat to pomocà tzv. for-cyklu (angl. for-loop):
for i in [0, 1, 2]: # hlaviÄka
print("ÄÃslo:", i) # tÄlo
Takový for-cyklus můžeme parafrázovat následovnÄ: každé ÄÃslo ze seznamu [0, 1, 2] postupnÄ ulož do promÄnné i a proveÄ sérii operacà popsaných v tÄle cyklu. ZkrácenÄ: pro každé (angl for each) ÄÃslo ze seznamu proveÄ následujÃcà operaci/operace.
HlaviÄka for-cyklu popisuje promÄnlivou Äást akce, kterou provádÃme (zde: promÄnná i postupnÄ nabývá různých hodnot, které Äerpáme ze seznamu). TÄlo for-cyklu popisuje repetitivnà Äást (zde: pokaždé promÄnnou i vytiskneme, spolu s prefixem "ÄÃslo: "). PodobnÄ jako u funkcà poznáme to, co jeÅ¡tÄ patÅà do tÄla for-cyklu, a to, co už ne, podle odsazenÃ:
for i in [0, 1, 2]:
print("ÄÃslo:", i)
print("Já jeÅ¡tÄ do tÄla cyklu patÅÃm!")
print("Já už ne :(")
ObecnÄ můžeme for-cyklus charakterizovat takto:
for item in iterable:
# do something with item
PromÄnná iterable může obsahovat jakýkoli iterovatelný objekt (= objekt, ze kterého lze tahat dalšà objekty, viz odd. Kolekce). Seznamy už jsme v roli iterable vidÄli v akci výše, n-tice fungujà stejnÄ. ÅetÄzce taky, pÅiÄemž ve for-cyklu je procházÃme znak po znaku:
ÅetÄzec = "abc"
for znak in ÅetÄzec:
print(znak)
Množiny fungujà podobnÄ, jen nenà zaruÄené poÅadÃ, v nÄmž budeme prvky vytahovat:
množina = {2, 3, 1}
for prvek in množina:
print(prvek)
U slovnÃků je to trochu složitÄjšà -- záležÃ, zda chceme procházet klÃÄe...
slovnÃk = {"a": 1, "b": 2, "c": 3}
for klÃÄ in slovnÃk:
print(klÃÄ)
# nebo též explicitnÄji
for klÃÄ in slovnÃk.keys():
print(klÃÄ)
... hodnoty...
for hodnota in slovnÃk.values():
print(hodnota)
... nebo obojà zároveÅ:
# jak pÅesnÄ tato syntax funguje si vysvÄtlÃme o kousek nÞ
for klÃÄ, hodnota in slovnÃk.items():
print(f"Pod klÃÄem {klÃÄ!r} je uložená hodnota {hodnota}.")
Ale iterovatelný objekt nerovná se nutnÄ jen kolekce. V kombinaci s for-cyklem se Äasto hodà funkce range:
for i in range(3):
print("ÄÃslo:", i)
Funkce range vytvoÅà objekt, který v hlaviÄce for-cyklu postupnÄ generuje ÄÃsla v zadaném rozpÄtà (v naÅ¡em pÅÃpadÄ od 0 do 3, hornà hranici vyjÃmaje). ProÄ je to užiteÄné? PÅedstavte si, že bychom chtÄli operaci print("ÄÃslo:", i) provést pro prvnÃch milion nezáporných ÄÃsel. Oproti seznamu má funkce range dvÄ výhody:
- pro nás je výhoda, že nemusÃme ruÄnÄ vypisovat seznam s milionem ÄÃsel
- pro poÄÃtaÄ je výhoda, že nemusà vytváÅet celý seznam najednou a pak ho uchovávat v pamÄti (milion ÄÃsel sice dneÅ¡nà poÄÃtaÄe zvládnou hravÄ, ale obecnÄ platÃ, že pamÄti nikdy nenà neomezenÄ) -- ÄÃsla tiskne jedno po druhém a jakmile jedno zpracuje, tak ho může zapomenout
Když promÄnnou z hlaviÄky for-cyklu nikde v tÄle nepoužijeme, bývá zvykem jà dát speciálnà jméno _, ÄÃmž ostatnÃm programátorům naznaÄÃme, že se nemajà divit, že nenà použitá.
# tÅi hody kostkou
for _ in range(3):
print(randint(1, 6))
ObÄas se stane, že potÅebujeme for-cyklem projÃt kolekci, jejÃmiž prvky jsou n-tice nebo seznamy, které v rámci for-cyklu potÅebujeme dále rozebrat na dÃlÄà prvky. Můžeme samozÅejmÄ použÃt indexaci:
# VÄtu "PrÅ¡Ã." máme reprezentovanou jako seznam dvou tokenů. Každý token
# je trojice ÅetÄzců: prvnà oznaÄuje slovnà tvar, druhý lemma (=
# slovnÃkovou podobu), tÅetà slovnà druh (V = sloveso, Z = interpunkce).
vÄta = [("PrÅ¡Ã", "prÅ¡et", "V"), (".", ".", "Z")]
for token in vÄta:
word = token[0]
lemma = token[1]
tag = token[2]
print(f"WORD: {word!r}, LEMMA: {lemma!r}, TAG: {tag!r}")
Práci nám ale může uÅ¡etÅit tzv. destrukturace (angl. destructuring, též se tomu nÄkdy ÅÃká unpacking nebo pattern matching): máme-li uspoÅádanou kolekci (seznam nebo n-tici) o X prvcÃch, můžeme prvky rovnou namapovat na X promÄnných:
a, b, c = [1, 2, 3]
a
b
c
Jak to funguje? Strukturnà vzorec (pattern) nalevo od = obsahuje tÅi volné "sloty" (odpovÃdajÃcà tÅem promÄnným). Python se tento vzorec pokusà pÅiložit na datovou strukturu napravo od = (pÅedstavte si průhledný pauzovacà papÃr), namapovat (match) objekty, které datová struktura obsahuje, na volné sloty, a strukturu tak rozebrat na dÃlÄà prvky (proto hovoÅÃme o destrukturaci nebo unpacking -- rozbalenÃ).
Ne vždycky se to samozÅejmÄ povede -- napÅ. když je slotů vÃc než prvků...
a, b, c = (1, 2)
... nebo naopak:
a, b = (1, 2, 3)
Strukturnà vzorec může obsahovat promÄnnou s hvÄzdiÄkou (viz odd. Funkce), pak na sebe tato promÄnná naváže zbývajÃcà prvky kolekce:
a, *rest = (1, 2, 3, 4, 5, 6)
a
rest
Když je kolekce vnoÅená, můžeme strukturnà vzorec namapovat jen na nejvyššà úroveÅ...
a, b = (1, (2, 3))
a
b
... nebo může vzorec být taktéž vnoÅený:
a, (b, c) = (1, (2, 3))
a
b
c
Když dáme destrukturaci dohromady s for-cyklem, zkrátà a zpÅehlednà se nám zápis:
for token in vÄta:
word, lemma, tag = token
print(f"WORD: {word!r}, LEMMA: {lemma!r}, TAG: {tag!r}")
A dokonce můžeme ubrat jeÅ¡tÄ jeden Åádek, protože destrukturaci lze provést pÅÃmo v rámci hlaviÄky for-cyklu:
for word, lemma, tag in vÄta:
print(f"WORD: {word!r}, LEMMA: {lemma!r}, TAG: {tag!r}")
Varianta s vnoÅeným strukturnÃm vzorcem může nastat napÅ. v pÅÃpadÄ, že si jednotlivé tokeny oÄÃslujeme pomocà funkce enumerate, která ke každému prvku ve zdrojové kolekci pÅidá poÅadové ÄÃslo. Takhle to vypadá, když si výsledek uložÃme do seznamu:
list(enumerate(vÄta))
A takhle bychom ji spolu s vnoÅenou destrukturacà mohli použÃt ve for-cyklu:
for i, (word, lemma, tag) in enumerate(vÄta):
print(f"{i + 1}. WORD: {word!r}, LEMMA: {lemma!r}, TAG: {tag!r}")
V prvnÃm opakovánà for-cyklu provede Python tuto destrukturaci...
i, (word, lemma, tag) = (0, ("PrÅ¡Ã", "prÅ¡et", "V"))
... v druhém pak tuto:
i, (word, lemma, tag) = (1, (".", ".", "Z"))
Na závÄr pÅÃklad použità destrukturace s hvÄzdiÄkou -- může se hodit, když jsou jednotlivé dÃlÄà kolekce, které postupnÄ ve for-cyklu zpracováváme, různÄ dlouhé:
for i, *rest in [(1,), (2, 3), (4, 5, 6)]:
print(i)
print(rest)
ObÄas se v rámci for-cyklů můžou hodit klÃÄová slovÃÄka break a continue. break okamžitÄ ukonÄà for-cyklus. NásledujÃcà for-cyklus by mÄl sice teoreticky projÃt ÄÃsla od 0 do 4, ale zastavà se u ÄÃsla 3:
for i in range(5):
# syntax podmÃnek viz odd. PodmÃnky
if i == 3:
break
print(i)
continue ukonÄà souÄasnou iteraci for-cyklu a zahájà dalÅ¡Ã. NásledujÃcà for-cyklus tedy projde ÄÃsla od 0 do 4, ale vytiskne jen ta lichá, protože u sudých se k Åádku s funkcà print vůbec nedostane:
for i in range(5):
if i % 2 == 0:
continue
print(i)
POZN.: V tÄlech for-cyklů jsme vÅ¡ude použili funkci print, abychom mohli nahlédnout do jejich průbÄhu, tj. do toho, jak se jednotlivé operace opakujÃ. Bez funkce print to jde samozÅejmÄ taky, jen se nám nedostane žádné vizuálnà zpÄtné vazby, která by nám pomohla pochopit, co se po spuÅ¡tÄnà for-cyklu odehrává:
num = 0
for x in range(101):
num += x
num
Jen jeÅ¡tÄ doplnÃm, že elegantnÄji můžeme ÄÃsla od 0 do 100 seÄÃst pomocà funkce sum:
sum(range(101))
Logika¶
V Pythonu lze pracovat i s logickými výroky. Základem pro to jsou konstanty True a False, které oznaÄujà pravdu, resp. nepravdu, a které Python vracÃ, když posuzuje pravdivost nÄjakého logického výroku:
# rovnost
2 + 2 == 4
list("abc") == sorted("cab")
# negace rovnosti
2 + 2 != 5
# pÅÃtomnost prvku v kolekci
"a" in "abc"
# pÅÃtomnost celého ÄÃsla v rozpÄtà celých ÄÃsel
7 in range(5, 10)
# identita objektu
jan = bratr = ["nohy", "trup", "hlava", "vlasy"]
jan is bratr
# různé typy nerovnostà -- menšà než
2 < 3
# menšà nebo rovno
3 <= 3
# vÄtÅ¡Ã
2 > 1
# vÄtšà nebo rovno
1 >= 1
1 < 3 > 2
# výroky o ÅetÄzcÃch
"abc".islower()
A tak podobnÄ, viz pÅÃklady operacÃ, které vracà True nebo False, v pÅedchozÃch oddÃlech.
Pravdivostnà hodnotu ale majà všechny objekty v Pythonu. Můžeme ji zjistit pomocà funkce bool:
bool("abc")
Pravdivé jsou skoro vÅ¡echny objekty kromÄ:
# ÄÃsla 0
bool(0)
# prázdných kolekcÃ
bool([])
bool({})
bool("")
# None
bool(None)
# a pochopitelnÄ samotné konstanty False
bool(False)
Sestavovat z jednoduchých výroků složitÄjšà lze pomocà operátorů and, or a not:
# x and y platÃ, když jsou oba výroky x a y pravdivé
True and True
True and False
# x or y platÃ, když je aspoÅ jeden z výroků x a y pravdivý
True or True
True or False
False or False
# not invertuje pravdivostnà hodnotu výroku
not True
not False
V praxi použijeme tÅeba následovnÄ:
ÅetÄzec = "koÄka"
len(ÅetÄzec) > 3 and "Ä" in ÅetÄzec
Jak jsme psali výše, pravdivostnà hodnotu majà vÅ¡echny objekty v Pythonu, můžeme tedy klidnÄ napsat:
not "abc"
True or 0
DÃky tomu můžeme trochu upÅesnit chovánà operátorů and a or. and zaÄne vyhodnocovat výrazy (zleva doprava) a vrátà buÄ prvnà nepravdivý, nebo poslednà zbývajÃcÃ:
# poslednà zbývajÃcà (pravdivý)
True and 1 and "abc" and sorted
# prvnà nepravdivý
True and 1 and "abc" and sorted and [] and 0
or taky zaÄne vyhodnocovat výrazy (zleva doprava) a vrátà buÄ prvnà pravdivý, nebo poslednà zbývajÃcÃ:
# prvnà pravdivý
0 or [] or False or 42 or "abc"
# poslednà zbývajÃcà (nepravdivý)
0 or [] or False or {}
Jakmile and nebo or narazà na výraz, podle kterého se může rozhodnout, dalšà už nevyhodnocuje. Můžeme si to ukázat, když jeden z výrazu bude volánà funkce, jejÃmž vedlejÅ¡Ãm efektem je, že nÄco vytiskne na obrazovku.
def funkce():
print("volám funkci...")
return "výsledek funkce"
# zde může and vrátit hned prvnà výraz, takže se funkce nezavolá
0 and funkce()
# zde je potÅeba po prvnÃm výrazu pokraÄovat v ovÄÅovánà pravdivostnÃch
# hodnot, takže se funkce zavolá
1 and funkce()
Pomocà or tedy můžeme napÅ. nahradit metodu setdefault (viz odd. Kolekce -- SlovnÃky) -- úpravu slovnÃku provedeme pouze v pÅÃpadÄ, že daný klÃÄ jeÅ¡tÄ neobsahuje:
slovnÃk = dict(a=1, b=2)
# "a" už ve slovnÃku je, prvnà výraz tedy platà a druhý se ani
# nevyhodnotÃ
"a" in slovnÃk or slovnÃk.update(a=42)
slovnÃk
# "c" ve slovnÃku nenÃ, prvnà výraz tedy neplatà a druhý se
# vyhodnotÃ
"c" in slovnÃk or slovnÃk.update(c=42)
slovnÃk
Máme-li kolekci objektů a chceme ovÄÅit, zda je aspoÅ jeden z nich pravdivý, můžeme použÃt zabudovanou funkci any:
any([True, False, False])
Chceme-li ovÄÅit, zda jsou vÅ¡echny pravdivé, použijeme zabudovanou funkci all:
all([True, True, True])
any a all jsou velmi užiteÄné v kombinaci s konvertory kolekcà (viz odd. Konvertory kolekcÃ).
Varovánà na závÄr -- složitÄjšà logické výroky jsou zrádné, jejich úpravy jsou netriviálnà a ÅÃdà se pÅesnÄ danými pravidly. Kdo si je podobnÄ jako já ze stÅednà školy pamatuje spÃÅ¡ matnÄ, mÄl by si dát extra pozor ;) NapÅ. výrok not (x and y) je ekvivalentnà výroku not x or not y, ne výroku not x and not y, jak bychom možná mohli mÃt tendenci naivnÄ "roznásobit".
SložitÄjšà pÅÃpady si naÅ¡tÄstà vždy lze pro jistotu ovÄÅit pomocà pravdivostnà tabulky, aÅ¥ už si ji nakreslÃme ruÄnÄ nebo si rovnost výroků pro vÅ¡echny možné kombinace hodnot x a y zkontrolujeme pomocà Pythonu:
for x in [True, False]:
for y in [True, False]:
# â výroky, jejichž rovnost ovÄÅujeme â
is_equal = (not (x and y)) == (not x or not y)
print(f"x is {x}, y is {y}, the equality is {is_equal}")
Aby rovnost dvou výrazů platila obecnÄ, musà platit pro každou variantu dosazenà konkrétnÃch hodnot za promÄnné (zde x a y). O tom se můžeme pÅesvÄdÄit buÄ ovÄÅenÃm výstupů funkce print (viz pÅedchozà buÅka), nebo můžeme kód taky upravit jako konvertor kolekce v kombinaci s funkcà all a dostaneme tak celkový výsledek rovnou:
all(
(not (x and y)) == (not x or not y)
for x in [True, False]
for y in [True, False]
)
A jeÅ¡tÄ pro srovnánÃ, jak by vypadaly výstupy v pÅÃpadÄ, že si porovnávané výrazy obecnÄ vzato rovné nejsou (vÅ¡imnÄte si, že pro nÄkteré kombinace hodnot x a y rovnost platÃ, ale ne pro vÅ¡echny)...
for x in [True, False]:
for y in [True, False]:
is_equal = (not (x and y)) == (not x and not y)
print(f"x is {x}, y is {y}, the equality is {is_equal}")
... takže celkový verdikt je...
all(
(not (x and y)) == (not x and not y)
for x in [True, False]
for y in [True, False]
)
Podmínky: if, elif, else (a while-cyklus)¶
Na logických výrocÃch lze dál stavÄt vÄtvenà programu pomocà podmÃnek:
if 2 + 2 == 4: # hlaviÄka
print("matematika funguje") # tÄlo
Syntax podmÃnek je znovu postavená na principu hlaviÄky a odsazeného tÄla, které se vykoná, pokud podmÃnka uvedená v hlaviÄce platÃ.
if je jako výhybka: platÃ-li podmÃnka, vydá se program trochu jinou cestou, než když neplatÃ. AlternativnÃch podmÃnÄných cest může být vÃce, napojÃme je pomocà klÃÄového slovÃÄka elif:
num = 2
if num == 0:
print("nula")
elif num == 1:
print("jedna")
elif num == 2:
print("dva")
Na závÄr můžeme pÅidat jeÅ¡tÄ jednu nepodmÃnÄnou alternativnà cestu pomocà klÃÄového slovÃÄka else. Tou se programu vydá, když se ukáže, že ani jedna z pÅedchozÃch podmÃnek neplatÃ:
num = 3
if num == 0:
print("nula")
elif num == 1:
print("jedna")
elif num == 2:
print("dva")
else:
print("nÄco jiného")
Je důležité si uvÄdomit, že vÄtvenà skuteÄnÄ funguje jako výhybka: program se vydá prvnà cestou, kde narazà na pravdivou podmÃnku, a zbývajÃcà ignoruje, i kdyby byly nakrásnÄ pravdivé taky:
if True:
print("podmÃnka u téhle vÄtve je vždy pravdivá")
elif True:
print("u téhle taky, ale nenà to nic platné, je až druhá")
VizuálnÄ si to můžeme pÅedstavit takto:
.---> if
/
/-----> elif
/
--------> elif
\
\-----{ ...
\
`---> else
Důležitým důsledkem je, že záležà na poÅadà podmÃnek. Pravidlo pravé ruky znÃ, že specifiÄtÄjšà podmÃnky by mÄly pro jistotu být na zaÄátku, jinak se k nim program nemusà dostat, protože ho odchytà dÅÃvÄjšà ménÄ specifické podmÃnky.
NapÅ. podmÃnka x >= 0 je ménÄ specifická než podmÃnka x == 2, takže když bude pÅed nÃ, sebere jà vÅ¡echny potenciálnà zákaznÃky:
for x in range(4):
if x >= 0:
print(f"{x} je vÄtšà než 0")
elif x == 2:
print("HA! DVOJKA.")
Srovnejte s výstupem, když poÅadà podmÃnek prohodÃme:
for x in range(4):
if x == 2:
print("HA! DVOJKA.")
elif x >= 0:
print(f"{x} je vÄtšà než 0")
Každé klÃÄové slovÃÄko if odstartuje nové vÄtvenÃ, pod nÄž spadajà pÅÃpadná následujÃcà elif a else na stejné úrovni odsazenÃ:
num = 2
if num > 1:
print("1. vÄtvenÃ: num je vÄtšà než jedna")
elif num < 1:
print("1. vÄtvenÃ: num je menšà než jedna")
else:
print("1. vÄtvenÃ: num je rovno jedné")
if num == 2:
print("2. vÄtvenÃ: num je rovno dvÄma")
VizuálnÄ si to můžeme pÅedstavit takto:
.---> if
/
/-----> elif
/
--------> elif
\
\-----{ ...
\
`---> else
--------> if
Existuje též speciálnà dvouÄlenný operátor if ... else, který jako výsledek vrátà jednu ze dvou možnostà podle toho, jak dopadne pravdivostnà test:
výsledek_je_li_test_pravdivý if test else výsledek_je_li_test_nepravdivý
Konkrétnà pÅÃklad bude asi srozumitelnÄjÅ¡Ã:
"A" if True else "B"
"A" if False else "B"
DÃky tomuto operátoru máme k dispozici kompaktnÄjšà zápis, když chceme hodnotu nÄjaké promÄnné stanovit na základÄ pravdivostnÃho testu. Bez operátoru if ... else bychom napÅ. museli psát:
ledniÄka = {"puding", "sýr", "salát"}
if "puding" in ledniÄka:
reakce = "hurá, puding!"
else:
reakce = "nenà puding :("
reakce
Ale s pomocà operátoru if ... else staÄà napsat:
reakce = "hurá, puding!" if "puding" in ledniÄka else "nenà puding :("
reakce
S podmÃnkami souvisà i jiná podoba cyklu, tzv. while-cyklus. While-cyklus se opakuje tak dlouho, dokud je podmÃnka v hlaviÄce pravdivá:
i = 0
while i < 3:
print("ÄÃslo", i, "je menšà než 3.")
i += 1
Konvertory kolekcí¶
Mnoho funkcà pracuje s kolekcemi, napÅ. zabudovaná funkce sorted nebo konstruktor FreqDist z knihovny nltk. NÄkdy je užiteÄné mÃt možnost kolekci trochu upravit ve chvÃli, kdy ji takové funkci pÅedáváme. K tomu pÅesnÄ sloužà konvertory kolekce.
Kamkoli můžeme dát normálnà kolekci...
vÄta = "Bylo nás pÄt .".split()
vÄta
sorted(vÄta)
... můžeme propaÅ¡ovat mÃsto nà i konvertor:
sorted(slovo for slovo in vÄta if slovo.islower())
Jejich syntax pÅipomÃná syntax for-cyklu, jen jsou jednotlivé prvky pÅeskládané a možnosti jsou omezenÄjšà než v plném for-cyklu. Abychom se v jejich zápisu lépe zorientovali, využijeme toho, že uvnitÅ jakýchkoli závorek můžeme kód v Pythonu libovolnÄ nasekat na Åádky a pÅidat odsazenÃ, aby se nám lépe Äetl:
sorted(
slovo
for slovo in vÄta
if slovo.islower()
)
TeÄ už je lépe vidÄt, že konvertor kolekce má tÅi Äásti. Ätou se odprostÅedka:
convert(item) # 3.
for item in collection # 1.
if test(item) # 2.
Popis jednotlivých fázà vypadá následovnÄ:
- ProcházÃme kolekci prvek po prvku.
- NepovinnÄ můžeme provést nÄjaký test; pokud ho aktuálnà prvek nesplnÃ, bude z výsledku vyÅazen. TÃm můžeme zdrojovou kolekci profiltrovat.
- Nakonec spoÄÃtáme hodnotu, kterou za daný prvek ze zdrojové kolekce zaÅadÃme do výsledku. Tato hodnota může být původnà prvek samotný, může být vypoÄÃtaná na základÄ prvku, nebo s nÃm taky vůbec nemusà souviset.
Nejjednoduššà konvertor kolekce, kterým kolekce projede nezmÄnÄná, vypadá takto:
item # 3.
for item in collection # 1.
# 2. nic
sorted(
slovo # 3.
for slovo in vÄta # 1.
# 2.
)
Chceme-li mÃsto slov samotných do výsledku zaÅadit n-tici (slovo, délka_slova), musÃme upravit fázi 3:
sorted(
(slovo, len(slovo)) # 3. úprava zde
for slovo in vÄta # 1.
# 2.
)
Chceme-li navÃc zahodit vÅ¡echna slova, která nesestávajà z malých pÃsmen, musÃme doplnit fázi 2:
sorted(
(slovo, len(slovo)) # 3.
for slovo in vÄta # 1.
if slovo.islower() # 2. úprava zde
)
Konvertor kolekce lze vždy pÅeskládat na normálnà for-cyklus, napÅ. ten bezprostÅednÄ pÅedcházejÃcÃ:
pomocný_seznam = []
for slovo in vÄta:
if slovo.islower():
pomocný_seznam.append((slovo, len(slovo)))
sorted(pomocný_seznam)
Naopak pÅeskládat for-cyklus na konvertor kolekce pokaždé nejde, protože for-cyklus poskytuje mnohem vÄtšà volnost. VýmÄnou za toto omezenà poskytujà konvertory kolekce oproti for-cyklům nÄkolik výhod:
- úspornÄjšà syntax (nesnažà se pokrýt plnou flexibilitu for-cyklů)
- pÅi bÄhu programu zabÃrajà ménÄ Äasu a pamÄti (napÅ. nenà potÅeba vytváÅet žádné pomocné kolekce typu
pomocný_seznam) - ve for-cyklu se kvůli jeho flexibilitÄ snadno může schovat mnohem vÄtšà množstvà chyb
Vztah konvertorů kolekcà k for-cyklům je tedy podobný jako vztah (nemodifikovatelných) n-tic k (modifikovatelným) seznamům: konvertory (a n-tice) jsou mnohem ménÄ flexibilnÃ, ale ve chvÃli, kdy tu flexibilitu nepotÅebujete, je dobré mÃt možnost se jà explicitnÄ vzdát a uchránit se tak možných chyb, které by z nà mohly plynout.
Když nenà konvertor kolekce jediným argumentem funkce, je potÅeba ho uzávorkovat...
sorted((slovo for slovo in vÄta if slovo.islower()), reverse=True)
... jinak nás Python vyplÃsnÃ:
sorted(slovo for slovo in vÄta if slovo.islower(), reverse=True)
Existuje speciálnà syntax na to, když chceme výsledky z konvertoru kolekce nasypat do seznamu (angl. se tomuto zápisu ÅÃká list comprehension)...
[slovo for slovo in vÄta if slovo.islower()]
... do množiny (angl. set comprehension)...
{len(slovo) for slovo in vÄta}
... nebo do slovnÃku (angl. dict comprehension):
{slovo: len(slovo) for slovo in vÄta}
U ostatnÃch kolekcà speciálnà syntax neexistuje, ale nenà proÄ si zoufat, jejich konstruktory vÄtÅ¡inou podporujà inicializaci na základÄ zdrojového iterovatelného objektu, takže staÄà vepsat konvertor kolekce do konstruktoru:
tuple(slovo.lower() for slovo in vÄta)
from nltk import FreqDist
FreqDist(slovo.lower() for slovo in vÄta)
Konvertory kolekcà jsou velmi užiteÄné v kombinaci s logickými funkcemi all a any (viz výše odd. Logika):
all(x < 10 for x in range(5))
any(x == 2 for x in range(5))
all(x == 2 for x in range(5))
Konvertorům kolekce též ÅÃkáme generátorové výrazy (angl. generator expression), podle toho, že jejich výsledkem je generátor -- objekt, který generuje dalšà objekty (na základÄ prvků původnà kolekce). Když ho vytvoÅÃme samostatnÄ, můžeme si ho i prohlédnout:
gen = (slovo for slovo in vÄta)
gen
type(gen)
Generátory posloužà kdekoli, kde je potÅeba iterovatelný objekt (podobnÄ jako kolekce nebo funkce range). NavÃc z nich lze prvky vytahovat po jednom pomocà zabudované funkce next:
next(gen)
Existujà dvÄ Å¡iroké kategorie iterovatelných objektů:
- kolekce
- a nÄco, co bychom mohli souhrnnÄ nazvat potenciálnà Äi virtuálnà kolekce (sem patÅà generátory, výstupy funkcÃ
range,reversed,enumerateapod.)
RozdÃl mezi reálnou kolekcà o milionu prvků a potenciálnà kolekcà o milionu prvků je v tom, že v pÅÃpadÄ reálné kolekce musà celý milion prvků zároveÅ fyzicky existovat v pamÄti poÄÃtaÄe. U potenciálnà kolekce staÄà mÃt recept, jak ten milion prvků vytvoÅit... až budou potÅeba.
Äasto nepotÅebujeme vÅ¡echny prvky kolekce najednou, staÄà nám je zpracovávat jeden po druhém. Pak jsou potenciálnà kolekce ideálnà -- zabÃrajà mnohem ménÄ pamÄti.
Jindy ale vÅ¡echny prvky najednou potÅebujeme, typicky když si je chceme prohlédnout. Pak nám potenciálnà kolekce moc neposloužÃ:
(slovo for slovo in vÄta)
enumerate(slovo for slovo in vÄta)
reversed([1, 2, 3])
range(-2, 2)
ÅeÅ¡enà je naÅ¡tÄstà jednoduché -- staÄà potenciálnà kolekci donutit, aby vygenerovala vÅ¡echny prvky, které v nà dÅÃmajÃ, a uložit je do reálné kolekce, napÅ. do seznamu:
list(slovo for slovo in vÄta)
list(enumerate(slovo for slovo in vÄta))
list(reversed([1, 2, 3]))
list(range(-2, 2))
Drobnosti¶
Práce se soubory¶
NejjednoduÅ¡eji se v Pythonu pracuje se soubory v podobÄ tzv. Äistého textu (angl. plain text; Äasto mÃvajà pÅÃponu .txt).
K otevÅenà souboru sloužà zabudovaná funkce open. Chceme-li do souboru zapisovat, je potÅeba specifikovat mód w (jako write):
text = "koÄka leze dÃrou\npes oknem"
with open("koÄka.txt", "w", encoding="utf-8") as file: # hlaviÄka
file.write(text) # tÄlo
Argument encoding je nepovinný, Python ho automaticky stanovà na základÄ nastavenà vaÅ¡eho operaÄnÃho systému (v podstatÄ vÅ¡ude kromÄ Windows to bude UTF-8). Vzhledem k tomu, že vaÅ¡e prvnà volba kódovánà by vždy mÄla být UTF-8 (viz notebook unicode.ipynb), je dobré si zvyknout ho vypisovat explicitnÄ a nenechat operaÄnà systém rozhodovat za vás.
Syntaxi s klÃÄovým slovÃÄkem with se ÅÃká context manager. V hlaviÄce zavoláme funkci open a jejà výsledek uložÃme do promÄnné file, která reprezentuje otevÅený soubor. TÄlo je pak kontext, v jehož rámci s tÃmto otevÅeným souborem můžeme pracovat (v naÅ¡em pÅÃpadÄ do nÄj zapisovat). Jakmile kontext skonÄà (= zruÅ¡Ãme odsazenÃ), Python za nás soubor automaticky zase zavÅe, aniž bychom museli ruÄnÄ volat metodu file.close(). Co vÃc, pokud v rámci tÄla nastane nÄjaký problém (chyba), tak Python soubor taky zavÅe, jeÅ¡tÄ než zkolabuje, abychom po sobÄ nenechali nepoÅádek.
Když chceme soubor znovu naÄÃst, můžeme pÅi jeho otevÃránà specifikovat mód r (jako read), ale nemusÃme, protože je to default. PÅi naÄÃtánà máme tÅi možnosti -- buÄ naÄteme celý soubor najednou jako jeden dlouhý ÅetÄzec...
with open("koÄka.txt", encoding="utf-8") as file:
text = file.read()
text
... nebo celý soubor najednou jako seznam Åádků...
with open("koÄka.txt", encoding="utf-8") as file:
lines = file.readlines()
lines
... nebo ho můžeme zpracovávat Åádek po Åádku pomocà for-cyklu:
with open("koÄka.txt", encoding="utf-8") as file:
for line in file:
print(line, end="")
Poslednà varianta se hodà zejména v pÅÃpadÄ, že máme velký textový soubor, který nepotÅebujeme (a tÃm pádem ani nechceme) naÄÃtat celý najednou do pamÄti.
NÄkdy jsou plaintextové soubory strukturované, takže naÄÃtat je Åádek po Åádku nenà úplnÄ nejlepšà způsob, jak s nimi pracovat. NapÅ. jupyterovské notebooky jsou ve formátu JSON, který se snažà reprezentovat základnà datové typy (ÄÃsla, ÅetÄzce, seznamy a slovnÃky) tak, aby Å¡ly vymÄÅovat mezi různými programovacÃmi jazyky.
Když naÄteme tento notebook jednÃm ze standardnÃch způsobů popsaných výše, tahle struktura se nám nevyjevÃ:
with open("python_crash_course.ipynb", encoding="utf-8") as file:
nb = file.readlines()
# prvnÃch deset Åádků souboru s notebookem
nb[:10]
Vypadne na nás jen seznam ÅetÄzců (= Åádků). Podle jejich obsahu se zdá, že tam nÄjaká struktura bude (vidÃme odsazenÃ, uvozovky, hranaté a složené závorky), ale nemůžeme s nà nijak pracovat, jediné, co máme k dispozici, jsou Åádky textu.
Na rozpoznánà struktury vÄtÅ¡iny bÄžných plaintextových formátů (tzv. parsovánÃ) naÅ¡tÄstà existujà knihovny:
import json
with open("python_crash_course.ipynb", encoding="utf-8") as file:
nb = json.load(file)
Pomocà knihovny json za nás Python naparsuje strukturu textového souboru a pÅevede ji do podoby, s nÞ umÃme pracovat (vnoÅené kolekce):
type(nb)
nb.keys()
nb["cells"][0]["source"]
A tak podobnÄ.
Pokud budete nÄkdy potÅebovat v Pythonu pracovat s jinými typy souborů než s Äistým textem (napÅ. Excel, Word atp.), na 99 % půjde vygooglit nÄjakou knihovnu, která vám s tÃm pomůže. TÅeba s excelovými tabulkami se dobÅe pracuje pomocà knihovny pandas.
Výrazy vs. příkazy¶
NÄkdy je užiteÄné mÃt jemnÄjšà terminologické rozliÅ¡enà pro různé Äásti kódu.
Výraz (angl. expression) je jakýkoli kousek pythonovského kódu, který lze vyhodnotit a jeho výsledek uložit do promÄnné. Jinými slovy, výraz je cokoli, co můžeme napsat napravo od operátoru =. VÅ¡echno ostatnà v Pythonu jsou pÅÃkazy (angl. statement).
PÅÃklady:
HlaviÄka for-cyklu (for x in y:) je pÅÃkaz -- sama o sobÄ nemá žádný výsledek, který by Å¡el uložit do promÄnné. KlÃÄové slovÃÄko for ale může být i souÄástà výrazu, konkrétnÄ generátorového výrazu, jehož výsledkem je generátor:
(x for x in range(3))
Podobný rozdÃl existuje mezi hlaviÄkou podmÃnkového vÄtvenà (if x:, elif y: i else: jsou vÅ¡echno pÅÃkazy) a operátorem if ... else, který je souÄástà výrazu:
42 if False else 0
A do tÅetice vÅ¡eho dobrého: pÅiÅazovánà promÄnné (napÅ. x = 3) je pÅÃkaz tvoÅený:
- jménem promÄnné (zde
x) - pÅiÅazovacÃm operátorem
= - a libovolným výrazem (zde ÄÃselný literál
3)
Dokumentace¶
Dokumentace programu sloužà k tomu, aby byl Äitelný nejen pro Python, ale i pro nás a dalšà programátory. To je důležité, protože když je program nesrozumitelný, tÄžko se upravuje / opravuje.
SložitÄjšà mÃsta v kódu si zasloužà komentáÅ. Je ale zbyteÄné komentovat každou operaci, zejména když je jejà zámÄr i výsledek naprosto jasný už z kódu:
# seÄÃst dvÄ jedniÄky a výsledek uložit do promÄnná dva
dva = 1 + 1
Funkce, které nejsou jen na jedno použitÃ, by mÄly mÃt dokumentaÄnà ÅetÄzec (angl. docstring).
def funkce():
"""Toto je dokumentaÄnà ÅetÄzec.
Prvnà Åádek by mÄl obsahovat struÄný popis, k Äemu funkce
sloužÃ. Dalšà mohou obsahovat detaily, charakteristiku
parametrů funkce atp.
"""
pass
DokumentaÄnà ÅetÄzec k libovolné funkci si lze zobrazit pomocà zabudované funkce help...
help(funkce)
... nebo v prostÅedà Jupyter pomocà ?:
funkce?
Inspiraci, jak psát užiteÄné docstringy, doporuÄuju hledat u funkcÃ, které sami použÃváte :)
sorted?
Comments
comments powered by Disqus