Příkazy
Na našem prvním příkladě pro výpočet největšího společného dělitele jsme viděli, že cíle lze dosáhnout rozdělením problému na menší části, které měly následující povahu
uschovávání hodnot (např. mezivýsledků) do proměnných
rozhodnutí, jak dále postupovat (která z proměnných a,b je větší?)
opakování souboru kroků
tisk informací pro uživatele (
print
)
To jsou příklady tzv. příkazů. Za desetiletí vývoje programování se ukázalo, že i nejuniverzálnější jazyk nepotřebuje takových příkazů jen několik a složitost programů spočívá v jejich vhodném složení do většího celku.
Příkazy a jejich posloupnosti
Již víme, že kód sestává z posloupnosti příkazů. Ty zapisujeme textem dodržujícím jistá pravidla. Pro ilustraci si ukažme, že téhož lze dosáhnout graficky, například v jazyce scratch diagram
popisuje kód v Pythonu vypadající takto:
a = 33
b = 121
while not a==b:
if a>b:
a = a-b
else:
b = b-a
Vidíme tedy, že místo skládání puzzle v Pythonu jen vhodně odsazujeme řádky s jednotlivými příkazy.
Přiřazovací příkaz
Oznamuje, že proměnná má nabývat hodnotu určenou nějakým výrazem.
promenna = vyraz
tedy například
energie = hmotnost * rychlost_svetla**2
V jazyce Python je ale přiřazovací příkaz také místem, kde proměnné vznikají. Prvním přiřazením do proměnné se s jejím jménem sváže hodnota určená výrazem na pravé straně znaku =
. Jak ještě několikrát zmíníme, proměnná nepředstavuje tak jako v jiných počítačových jazycích popisku nějakého místa paměti pro uložení dat, ale odkaz na taková data. Dalším přiřazením do téže proměnné se tedy změní nejen hodnota, ale také místo, kde v paměti data sídlí. To je velmi důležité při práci s poli a tam
se k tomuto faktu znovu vrátíme.
V důsledku přiřazení se může změnit i typ dat.
Pokročilejší výklad
Chování přiřazení si můžeme demonstrovat za pomoci zabudované funkce Pythonu id(...)
, která sděluje na jaké adrese jsou data proměnné uložena.
[9]:
x = 1.2
print(f"{id(x)=}")
y = x
print(f"{id(y)=}")
x = x/y
print(f"{id(x)=}")
x = 1.2
print(f"{id(x)=}")
id(x)=140717650747248
id(y)=140717650749072
id(x)=140717650741360
id(x)=140717650740496
Povšimněte si, že přiřazení x=y
způsobí, že obě proměnné odkazují na tatáž data. Pokud bychom použili x=y+0
, nebylo by tomu tak, ačkoli by x a y nabývaly stejné hodnoty. Stejné adresy tedy znamenjí více než stejné hodnoty.
Ke zjištění totožnosti slouží operátor
is
Ke zjištění rovnosti slouží operátor
==
[13]:
x1 = 1.2
y1 = x1
x2 = 1.2
y2 = x2+0
print(f'{x1 is y1 = }')
print(f'{x2 is y2 = }')
print()
print(f'{x1 == y1 = }')
print(f'{x2 == y2 = }')
x1 is y1 = True
x2 is y2 = False
x1 == y1 = True
x2 == y2 = True
Další komplikace je v tom, že z hlediska is
se celá čísla a řetězce chovají zcela resp. poněkud jinak než čísla reálná:
[12]:
x1 = 1.0
x2 = 1.0+0
print( f"{x1 is x2 = }" )
s1 = "ab"
s2 = "a"+"b"
print( f"{s1 is s2 = }" )
i1 = 10
i2 = 10+0
print( f"{i1 is i2 = }" )
i1 = 400
i2 = 400+0
print( f"{i1 is i2 = }" )
x1 is x2 = False
s1 is s2 = True
i1 is i2 = True
i1 is i2 = False
Konec pokročilejšího textu
Vícenásobné přiřazení
Velmi pohodlná je varianta přiřazovacího příkazu zahrnujícího dvě nebo i více přiřazení zároveň. Její užitečnost si můžeme demonstrovat na prohození obsahu dvou proměnných. Uvidíme, že v programování jde o častý úkaz.
Obvyklý způsob, jak prohodit dvě proměnné je
pom_prom = a
a = b
b = pom_prom
Python umožňuje to zapsat přehledněji
a, b = b, a
Protože takový zápis zpřehleňuje kód lze jej doporučit, i když, samozřejmě, nejde o zjednodušení z hlediska rychlosti operací.
Pozn. V našem kurzu nebude příliš času si vysvětlit, že jde o techniku související se složeným datovým typem n-tic (tuple) a jejich rozbalováním (unpacking) v rámci přiřazení.
Příkaz import
Již jsme potkali konstrukci
import math
Jde o příkaz, který zařídí, že můžeme používat funkce z této knihovny za použití zápisu print( math.sin(math.pi/3) )
a pod.
Pro úplnost uveďme, že tento příkaz se může vyskytovat v dalších podobách. Například
from math import sin, cos, pi
nám dovolí používat ještě jednoušší podobu zápisu print( sin(pi/3) )
Ještě častěji potkáme variantu
import numpy as np
která nám umožní místo delšího numpy
psát kratší np
. Drobnost, ale velmi používaná a srozumitelná.
Příkaz print
a volání podprogramu
Dávno již používáme příkaz print
, např.
print( x, x*x, x*x-a )
by nám srozumitelně sdělilo, jak dobře jsme nedávno hledali odmocniu z a
.
Jde o speciální variantu příkazu, kterému se odborně říká výrazový příkaz, ale pro jednoduchost budeme teď používat termín volání podprogramu (jde o podmnožinu výrazových příkazů skládajících se právě z jedné funkce).
Podprogram (procedura jak uvidíme vlastně funkce) print
je již připravený kus programu, který vezme zadané agumenty a zařídí, že se jejich textová podoba objeví na požadovaném místě (zatím v konzoli nebo jako výstup buňky v sešitu Jupyter).
Jako jiný příklad výrazového příkazu použijme funkci input
. Ta sice vrací řetězec, který po výzvě zadáme na klávesnici, nicméně můžeme tuto vrácenou hodnotu ignorovat a psát
[19]:
input('stikněte klávesu <enter>')
print('děkuji')
děkuji
Jde o program s dvěma příkazy, oba mají podobu volání podprogramu.
Větvení - podmíněný příkaz if
Jako obvykle máme jednoduchou variantu s jednou větví
if podminka:
prikaz1
prikaz2
....
a variantu s
if podminka:
prikaz1
prikaz2
....
else:
prikazA
prikazB
....
Použití odsazování (indentace) pro podpříkazy strukturovaných příkazů si vyžádalo ještě variantu s elif
if podminka:
prikaz1
prikaz2
....
elif podminka2:
prikazA
prikazB
....
Tu lze kombinovat s else
na konci řetězce podmínek.
Uveďme konktétní příklad:
potřebujeme-li prohodit dvě proměnné tak, aby bylo \(a\ge b\), můžeme napsat
if a < b:
a, b = b, a
Cyklus while
while podminka:
prikaz1
prikaz2
....
Pokud podmínka není splněna hned na počátku, žádný z příkazů těla cyklu se neprovede.
Jde o klíčový příkaz, který umožní opakovat jisté operace, dokud nedosáhneme požadovaného stavu určeného podmínkou. Například:
s = 0
n = 1
while n < 1000:
s = s + 1/n
n = n * 2
spočte součet \(1+\frac{1}{2}+\frac{1}{4}+...+\frac{1}{512}\).
Důležité: mezi příkazy těla cyklu by neměl chybět takový, který někdy změní hodnotu podmínky. Jinak se bude cyklus opakovat bez přestání do té doby, dokud nenastane nějaká chyba, běh programu nepřerušíme nebo neukončíme. (Rozdíl mezi přerušit a ukončit ještě potkáme.)
Cyklus for
V počítačových jazycích býval důležitý cyklus, který zařídil, že nějaká proměnná v cyklu nabývala hodnoty z nějakého intervalu celých čísel. Tvůrci počítačových jazyků ovšem tento koncept různě rozšiřovali. V Pythonu je to míněno takto: Vezmi nějaký seznam hodnot a zařiď že tzv. řídící proměnná cyklu postupně nabude všech hodnot z tohoto seznamu. To se později ještě zdokonalilo v tom, seznam může být definován jen předpisem, jak takové hodnoty zjistit, ale není nezbytné jej skutečně vytvářet. To
umožní, že počet opakování cyklu for
může být vyšší, než kolik je dostupné paměti na uložení takového seznamu.
Na počátku našeho kurzu se ovšem vrátíme k základům programování a ukážeme si jak vypsat seznam celých čísel od 1 do 100:
for i in range(1,101):
print(i)
Hlavička cyklu v podobě for i in range(1,101):
zařídí, že bude provedena následující sekvence operací
i = 1
print(i)
i = 2
print(i)
i = 3
print(i)
...
i = 99
print(i)
i = 100
print(i)
Musíme vzít na vědomí, že funkce range
se chová tak, že její první argument představuje hodnotu, kterou se začne, zatímco druhý argument hodnotu před kterou se skončí. Je to nepohodlné, jak ale uvidíme, v rámci jazyka Python odůvodněné. Můžeme ted psát, že základní varianta cyklu for
má podobu
for index in range(initial, final+1):
prikaz1
prikaz2
....
Zajímavým detailem takovéto podoby cyklu for je fakt, že na jeho konci nabývá i
hodnoty final
, zatímco svojí funkcí podobný cyklus
i = initial
while index < final:
prikaz1
prikaz2
....
index = index + 1
skončí i
s hodnotou final+1
(v obou případech předpokládáme initial < final
).
Funkce range
může mít jen jeden argument, tj. range(stop)
, potom např.range(5) -> 0,1,2,3,4
.
Funkce range může mít i třetí argument, tj. range(start,stop,step)
, potom např.range(2,12,2) -> 2,4,6,8,10
.
Cyklus for
je velmi důležitý i v této základní podobě. Proto si vyzkoušejte napsat takové argumenty range
aby následující kód vypsal: - 1,2,3 - 1,3,5,7 - -4,-3,-2,-1 - -1,-2,-3,-4 (použijte argument step=-1)
[15]:
# prostor pro testování cyklů for
for i in range(2,12,2):
print(i,end=' ')
2 4 6 8 10
Pozn. Ve skutečnosti je range(...)
funkce, která vrací návod, jak a jaký interval má proměnná i
procházet. Místo ní může být jakýkoli výraz u kterého dává smysl pocházet jeho hodnoty, např. seznam:
seznam = [1,2,3]
for x in seznam:
print(x)
Vnořené cykly
Protože příkazy můžeme kombinovat, lze v jednom cyklu mít cyklus další. Je vhodné použít jinou řídící proměnnou, výjimky z tohoto pravidla patří do prokročilého programování.
[18]:
# malá násobilka
for i in range(1,11):
for j in range(1,11):
print(f'{i*j:4}',end='')
print()
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
Příkaz skoku break
Pokud je cyklus svojí povahou složitější, může bý výhodné zjisťovat podmínku jeho ukončení nejakým výpočtem uvnitř a následně použít příkaz break
, který vyskočíz cyklu a běh programu pokračuje dalším příkazem, jaký by následoval po obvyklém ukončení cyklu.
Cyklus repeat
Příkaz cyklu testující podmínku na konci cyklu nikoli na před jeho započetím (jako je repeat
v Pascalu nebo do
v C) není v Pythonu k dipozici. Místo toho můžeme použít nekonečný cyklus while True:
a jeho opakování ukončit po tesu na konci. Například
while True:
x = int( input('zadejte kladné číslo:') )
if x>0:
break
print('děkuji')
Cvičení: přepište tento kód bez použití break
a rozmyslete si, zda vám to přijde elegantnější. Pozn. je více možností, jedna např. opakuje volání funkce input
, jiná nastaví na počátku vhodnou hodnotu x
.
Indentace (odsazování) kódu
Viděli jsme, že např. při použití příkazu while
potřebujeme odlišit, které příkazy se mají opakovat a které provádět po skončení cyklu, a že za tím účelem jsou příkazy těla cyklu odsazeny.
Máme tu dvě pravidla
Jako nápověda, že máme uvažovat o změně odsazování slouží dvojtečka na konci řádků s příkazy
if
,else
,elif
,while
,def
atd.Odsazení podřízených příkazů musí být větší a stejné. Doporučuje se používat stejné odsazení v podobě čtyř mezer.
Komplikaci představuje neviditelný znak <tab>
, který odskakuje na začátek dalšího tabulačního sloupce. Jeho šířka je obvykle osm znaků. Python nedovoluje kombinovat tabulátory a mezery:
tomas@dronte:~/tmp/prog$ python3 spaces.py
File "/home/tomas/tmp/prog/spaces.py", line 3
print(2)
TabError: inconsistent use of tabs and spaces in indentation
tomas@dronte:~/tmp/prog$ cat spaces.py
if 1 < 2:
print(1)
print(2)
tomas@dronte:~/tmp/prog$ hexdump -C spaces.py
00000000 69 66 20 31 20 3c 20 32 3a 0a 09 70 72 69 6e 74 |if 1 < 2:..print|
00000010 28 31 29 0a 20 20 20 20 20 20 20 20 70 72 69 6e |(1). prin|
00000020 74 28 32 29 0a |t(2).|
00000025
tomas@dronte:~/tmp/prog$
Tento záznam komunikace s počítačem obsahuje tři příkazy
pokus o spuštění programu
spaces.py
. Ten skončí výpisem chybyTabError: inconsistent use of tabs and spaces in indentation
vypsání obsahu soubory
spaces.py
příkazemcat
, které neukazuje nic podezřeléhopodrobné vypsání obsahu soubory
spaces.py
příkazemhexdump
, které ukazuje , že řádekprint(1)
začíná tabulátorem (ascii znak 09), nikoli osmi mezerami.
Závěr: Pro indentaci příkazů se doporučuje používat jen mezery.
Pokračování řádků
Někdy se vše nevejde na jeden řádek. Ve fyzice to jsou zejména dlouhé výrazy. Možností je více, nám stačí jediné - výraz rozdělte uvnitř závorek. Následující příklad m.j. ukazuje, že odsazení pokračovacího řádku může být libovolné a nemusí se podřizovat příkazu if
.
if abs(x) < 0.2:
y = x*(1 + x2*(-0.3333333333333333 + x2*(0.2
+ x2*(-0.14285714285714285 + x2*0.1111111111111111 ))))
Více příkazů na jeden řádek
Někdy je žádoucí mít na jednom řádku více příkazů. V tom případě slouží k oddělení příkazů středník. Používejte sřídmě.
Kontrolní úlohy
Opravte indentaci v programu pro malou násobilku
# malá násobilka for y in range(1,11): for x in range(1,11): print(f'{x*y:4}',end='') if x==1: print(' |',end='') if y==1: print() print('-----+------------------------------------',end='') print()
tak, aby jeho výstup byl
1 | 2 3 4 5 6 7 8 9 10 -----+------------------------------------ 2 | 4 6 8 10 12 14 16 18 20 3 | 6 9 12 15 18 21 24 27 30 4 | 8 12 16 20 24 28 32 36 40 5 | 10 15 20 25 30 35 40 45 50 6 | 12 18 24 30 36 42 48 54 60 7 | 14 21 28 35 42 49 56 63 70 8 | 16 24 32 40 48 56 64 72 80 9 | 18 27 36 45 54 63 72 81 90 10 | 20 30 40 50 60 70 80 90 100
Povšimněte si, že
x==1
se testuje uvnitř cyklufor x in ...
, zatímcoy==1
se testuje v rámci cyklufor y in ...
. Posledníprint()
ukončuje každý z vytištěných řádků.