![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
K napsání užitečného programu potřebujeme mít možnost měnit chování programu v závislosti na jistých podmínkách. Tuto možnost nám dávají podmíněné příkazy
Touto podmínkou realizujeme tak zvané alternativní provedení, při němž existují dvě možnosti a podmínka určí, která z nich se provede:
x = int(input("Zadej číslo: ")) if x % 2 == 0: print (x, "je sudé") else: print (x, "je liché")
Je-li zbytek po dělení dvěma roven nule, potom víme, že x je sudé a program tuto zprávu zobrazí. Není-li tato podmínka pravdivá, provede se druhá sada příkazů. Protože podmínka může být pouze pravdivá nebo nepravdivá, provede se určitě jedna z obou možností.
Alternativám říkáme větve protože jsou rozvětvením toku programu.
Mimochodem, kdybychom potřebovali často posuzovat "lichost" či "sudost" čísel, mohli bychom si tento prográmek "zabalit" do funkce:def print_parity(x): if x%2 == 0: print(x, "je sudé") else: print(x, "je liché")
Pro každou hodnotu x vrátí print_parity příslušnou zprávu. Při volání můžeme zadat jako argument jakýkoliv celočíselný výraz:
>>> print_parity(17) 17 je liché >>> y = 41 >>> print_parity(y + 1) 42 je sudé
Realizace alternativního provedení má zkrácenou verzi, která spočívá v tom, že vypustíme část else
:
x = int(input("Zadej číslo: ")) if x >= 0: print ("odmocnina", x, "=", x**0.5) print ("Nevadí, jedeme dál.")
Stejně jako funkce a jiné složené příkazy se příkaz if skládá ze záhlaví a těla. Záhlaví začíná klíčovým slovem if, pokračuje booleovským výrazem neboli podmínkou a končí dvojtečkou.
Poté, co se podmínka vyhodnotí jako pravdivá, provede se odsazená část složeného příkazu.
Není-li podmínka splněna, pokračuje tok výpočtu na prvním následujícím neodsazeném řádku, který již není součástí podmínky a provede se nezávisle na splnění či nesplnění podmínky if..
Někdy je více než dvě možností a my potřebujeme více než dvě větve. Použijeme seriově uspořádané (zřetězené) podmínky:
if x < y: print(x, "is less than", y) elif x > y: print(x, "is greater than", y) else: print(x, " and ", y, " are equal")
elif je zkratka "else if". Opět je provedena pouze jedna větev. Počet elif není omezen, příkaz else však smí být pouze jeden a musí být uveden jako poslední.
Podmínky jsou zkoumány jedna za druhou. Je-li první nepravdivá, prověřuje se další. Je-li některá pravdivá, provede se bezprostředně následující větev programu. Jestliže je pravdivých podmínek více, provede se jen první z nich.
Jedna podmínka může být vnořena do druhé. Předchozí trojité větvení můžeme realizovat také takto:
if x == y: print(x, "and", y, "are equal") else: if x < y: print(x, "is less than", y) else: print(x, "is greater than", y)
Vnější podmínka má dvě větve. První větev obsahuje prostý příkaz k tisku. Druhá větev obsahuje další podmínku if, která má své vlastní dvě větve. Obě tyto větve jsou prostým voláním funkce print.
I když odsazení příkazů činí strukturu zápisu zřejmou, vnořené podmínky se velmi rychle stávají méně přehledné. Pokud můžeme, tak se jim raději vyhneme.
Vnořené podmínky můžeme často zjednodušit logickými operátory. Následující kód bude v další ukázce přepsán pro použití jen jedné podmínky:
if 0 < x: if x < 10: print("x is a positive single digit.")
K volání funkce print dojde pouze tehdy, splníme-li obě podmínky, můžeme tedy použít operátor and.
if 0 < x and x < 10: print("x is a positive single digit.")
Pro toto docela běžné seskupení podmínek poskytuje Python alternativní syntaxi, která je podobná zápisu v matematice:
if 0 < x < 10: print("x is a positive single digit.")
Tato podmínka je významově stejná jako složený booleovský výraz s vnořenou podmínkou.
Příkaz return s výhodou použijeme ve funkci s větvenými podmínkami, jako například:
def absolute_value(x): if x < 0: return -x else: return x
Protože jsou tyto příkazy v alternativních podmínkách, provedou se pouze jednou - jakmile se jeden z příkazů return provede, funkce končí bez provádění následných příkazů.
Naši funkci můžeme zapsat také stručněji. Místo příkazu else dáme přímo příkaz return.
def absoluteValue(x): if x < 0: return -x return x
Přesvědčme se, že tato fce pracuje stejně jako ta předchozí.
Kód, který je zapsán za příkazem return i kterékoliv další místo, kam se tok programu nedostane, se nazývá mrtvý kód.
Je dobré aby každá možná cesta funkcí narazila na příkaz return. V následující verzi absolute_value toto zajištěno není:
def absolute_value(x): if x < 0: return -x elif x > 0: return x
Tato verze není zcela správná, protože bude-li x rovno nule, žádná z podmínek není pravdivá a funkce končí, aniž by narazila na příkaz return; výslednou hodnotou je speciální hodnota zvaná None:
>>> print(absoluteValue(0)) None
None je jedinečná hodnota typu NoneType:
>>> type(None) <class 'NoneType' >
Vestavěná funkce bool vrací booleovskou hodnotu čísel a řetězců:
>>> bool(1) True >>> bool(0) False >>> bool("No !") True >>> bool("") False >>> bool(3.14159) True >>> bool(0.0) False
U číselných typů generují nulové hodnoty výstup False, nenulové hodnoty výstup True. Prázdné řetězce dávají False, neprázdné řetězce dávají True.
Za booleovskou funkci lze považovat jakákokoliv funkci, která vrací hodnotu True nebo False:
def is_divisible(x, y): if x % y == 0: return True else: return False
Jméno této funkce je is_divisible (je dělitelné). Je obvyklé dávat booleovským funkcím jména, která zní jako otázka "ano či ne". Funkce is_divisible vrací buď True nebo False podle toho, zda x je či není dělitelné y.
Funkci můžeme ještě zjednodušit využitím faktu, že podmínka příkazu if je sama booleovským výrazem. Výsledek získáme přímo i po vypuštění příkazu if:
def is_divisible(x, y): return x % y == 0
Následující seance předvádí novou funkci v akci:
>>> is_divisible(6, 4) False >>> is_divisible(6, 3) True
Booelovské funkce jsou často používány v podmíněných příkazech:
if is_divisible(x, y): print("x is divisible by y") else: print("x is not divisible by y")
Nyní bychom měli být schopni poznat pouhým pohledem na zápis funkce co má provádět. Pokud jsme prováděli cvičení, sami jsme několik menších funkcí napsali. Při psaní větších funkcí nám mohou nastat potíže kvůli významovým chybám a chybám při běhu programu.
Abychom se vyrovnali s rostoucí složitostí programů seznámíme se s technikou, zvanou přírůstkový rozvoj. Jeho cílem je vyloučení dlouhých seancí při odstraňování chyb tím, že se postupně přidávají a testují krátké úseky kódu.
Jako příklad předpokládejme, že chceme nalézt vzdálenost dvou bodů, daných souřadnicemi (x1, y1) a (x2, y2). Vzdálenost podle Pythagorovy věty je:
Nejprve musíme uvážit jak by měla funkce distance vypadat. Jinými slovy, jaké budou vstupy (parametry) a jaký bude výstup (hodnota return)?
V našem případě musíme na vstupu zadat čtyři souřadnice dvou bodů. Výstupní hodnotou bude jejich vzdálenost s hodnotou typu float.
Již můžeme psát obrys funkce:
def distance(x1, y1, x2, y2): return 0.0
Je zřejmé, že tato verze žádnou vzdálenost nepočítá, neboť vždycky vrátí nulu. Je ale skladebně (syntakticky) správná, poběží a můžeme jí testovat před tím než ji zkomplikujeme.
Abychom ji otestovali, zavoláme ji pro jednoduché hodnoty:
>>> distance(1, 2, 4, 6) 0.0
Hodnoty jsme vybrali tak, aby vodorovná vzdálenost bodů byla 3, svislá 4 a výsledná vzdálenost 5 (což je přepona pravoúhlého trojúhelníka). Při testování funkce je užitečné znát správný výsledek předem.
V této chvíli jsme se přesvědčili, že funkce je syntakticky správná a můžeme začít přidávat řádky kódu. Po každé malé změně funkci znovu otestujeme. Objeví-li se v kterémkoli místě chyba, budeme vědět kde musí být – v posledním přidaném řádku.
Prvním logickým krokem ve výpočtu bude nalézt rozdíly x2-x1 a y2-y1. Tyto hodnoty uložíme do dočasných proměnných dx, dy a vytiskneme je.
def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print("dx is", dx) print("dy is", dy) return 0.0
Je-li funkce v pořádku, měly by výsledky být 3 a 4. Jestliže ano, ověřili jsme si, že náš dílčí program pracuje správně. Pokud ne, potřebujeme prověřit jenom několi málo řádků.
Nyní sečteme součet čtverců pro dx a dy.
def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dsquared = dx**2 + dy**2 print("dsquared is: ", dsquared) return 0.0
Všimněme si, že jsme odstranili evokace funkce print, které jsme zapsali v předchozím kroku. Takovéto dočasné části kódu říkáme brlení (scaffolding), protože pomáhá při sestavení programu, ale není součástí finálního výsledku.
Opět necháme proběhnout program a zkontrolujeme výstup (což by mělo být 25).
Konečně, s použitím zlomkového exponentu 0.5 pro nalezení odmocniny, můžeme spočítat výsledek:
def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dsquared = dx**2 + dy**2 result = dsquared**0.5 return result
Pokud nám to chodí správně, jsme hotovi.
Když začínáme, měli bychom přidávat jen po jednom či dvou řádcích. Po získání jisté zkušenosti můžeme přidávat po větších dávkách. Přírůstkový rozvoj nám v každém případě ušetří mnoho času při odstraňování chyb.
Klíčové aspekty tohoto postupu jsou:
Alternativní tvar funkce pro určení vzdálenosti dvou bodů jsme vytvořili již v kapitole 3.8:
import math def distance(x1,y1, x2,y2): return math.sqrt((x2-x1)**2 + (y2-y1)**2)
V kapitole 3.6 a 3.10 jsme definovali funkci cat_twice, která obsahovala volání funkce print_twice.
Abychom mohli sledovat, která proměnná je ve které funkci použita, ukážeme si schematické zobrazení zásobníku (stack diagram).
Každá funkce je reprezentována svým rámcem. Rámec tvoří jméno funkce s přilehlým obdélníkem, ve kterém jsou uvedeny její parametry a proměnné. Schéma zásobníku pro předchozí příklad vypadá takto:
Uspořádání schematu ukazuje průběh provádění. Funkce print_twice byla volána funkcí cat_twice a cat_twice byla volána funkcí __main__, což je speciální jméno pro nejvrchnější funkci. Když vytvoříme proměnnou vně jakékoliv funkce, patří do __main__!
Každý parametr se vztahuje k téže hodnotě jako jeho odpovídající argument. Tudíž part1 má stejnou hodnotu jako chant1, part2 má stejnou hodnotu jako chant2 a bruce má stejnou hodnotu jako cat.
Objeví-li se chyba při volání funkce, vytiskne Python jména všech funkcí, které se na tomto volání podílely; počínaje poslední, konče první zúčastněnou.
Vyzkoušejme si to. Vytvoříme skript se jménem test2.py, který vypadá takto:
def print_twice(bruce): print(bruce, bruce) print(cat) def cat_twice(part1, part2): cat = part1 + part2 print_twice(cat) chant1 = "Pie Jesu domine, " chant2 = "Dona eis requim." cat_twice(chant1, chant2)
Do funkce print_twice jsme přidali volání print (cat), ale tam není proměnná cat definována. Běh skriptu vyvolá takovéto chybové hlášení:
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in cat_twice File "<module>", line 3, in print_twice NameError: name 'cat' is not defined
Tento seznam funkcí se nazývá zpětný záznam (traceback). Říká nám ve kterém souboru se chyba vyskytla, který řádek a které funkce byly právě prováděny. Také označí číslo řádku na kterém k chybě došlo
Všimněme si podobnosti mezi zpětným záznamem a zobrazením zásobníku, není náhodná.
Dobrá čitelnost programu je pro programátora velmi důležitá, protože programy jsou častěji čteny a upravovány než psány. Všechny příklady kódů v této knize jsou psány ve shodě s manuálem Python Enhancement Proposal 8 (PEP 8), v němž jsou uvedeny pokyny pro stylové úpravy kódu.
Až naše programy budou rozsáhlejší, budou i naše potřeby stylu naléhavější ale několik pokynů bude užitečných už teď:
Při rozvoji programu se s oblibou provádí testování vybraných úseků zdrojového kódu. Pro toto testování poskytuje Python moduly doctest a unittest.
Popíšeme si práci s modulem doctest. Zkoumané vzorky kódu se umístí do dokumentačního řetězce pod záhlavím funkce. V každém vzorku je na prvním řádku kód, jakoby zadaný v interaktivním režimu, na druhém řádku je očekávaná odezva.
Modul doctest automaticky spustí příkaz začínající >>> a jeho výstup porovná s následujícím řádkem.
Následující ukázku si uložte do souboru ch05_doctest.py, který spustíte z příkazového řádku konzoly:
def is_divisible_by_2_or_5(n): """ >>> is_divisible_by_2_or_5(8) True >>> is_divisible_by_2_or_5(7) False >>> is_divisible_by_2_or_5(5) True """ return n % 2 == 0 or n % 5 == 0 if __name__ == '__main__': import doctest doctest.testmod()
Poslední tři řádky spouští celou parádu. Umisťujeme je na konec každého souboru, který obsahuje doctesty.
Pokud procedura zjistí shodu mezi zkoumanými vzorky a zadanými výsledky, reaguje "mlčením". Pokud narazí na rozpor, spustí rozsáhlé chybové hlášení, zejména doplníme-li evokaci skriptu atributem -v (verbose).
Spuštění neúspěšného skriptu vyprodukuje například následující výstup:
> python ch05_doctest.py ****************************************************************** File "ch05_doctest.py", line 3, in __main__.is_divisible_by_2_or_5 Failed example: is_divisible_by_2_or_5(8) Expected: True Got nothing ****************************************************************** 1 items had failures: 1 of 1 in __main__.is_divisible_by_2_or_5 ***Test Failed*** 1 failures.
return |
záznam zásobníku , protože probírá funkce v pořadí, ve kterém byly do zásobníku běhu programu zařazeny (runtime stack).
|
Všechna cvičení budou postupně přidávána do souboru se jménem ch05.py. Na konec totoho skriptu se umístí následující kód:
if __name__ == '__main__': import doctest doctest.testmod()Po zapsání každého cvičení spustíme skipt, abychom se přesvědčili, že naše nová funkce projde testem.
def compare (a, b): """ >>> compare(5,4) 1 >>> compare(7,7) 0 >>> compare(2,3) -1 """ # Zde napište svou fci
def hypotenuse (a,b): """ >>> hypotenuse(3,4) 5.0 >>> hypotenuse(12,5) 13.0 >>> hypotenuse(7,24) 25.0 """ # Zde napište svou fci
def slope (x1,y1,x2,y2): """ >>> slope(5,3,4,2) 1.0 >>> slope(1,2,3,2) 0.0 >>> slope(1,4,1,2) kolmice k ose 'x' Ošetřená výjimka! >>> slope(2,4,1,2) 2.0 """ # Zde napište svou fciFunkci slope použijte v další funkci intercept(x1,y1,z2,y2), která vrací ypsilonovou pořadnici přímky, procházející bodem (x1,y1) a (x2,y2).
def intercept (x1,y1,x2,y2): """ >>> intercept(1,6,3,12) 3.0 >>> intercept(6,1,1,6) 7.0 >>> intercept(4,6,12,8) 5.0 """ # Zde napište svou fci
def is_factor (f,n): """ >>> is_factor(3,12) True >>> is_factor(5,12) False >>> is_factor(2,14) True """ # Zde napište svou fci
def is_multiple( (m,n): """ >>> is_multiple(12,3) True >>> is_multiple(12,4) True >>> is_multiple(12,5) False """ # Zde napište svou fciŠlo by použít fci is_factor při definici fce is_multiple?
def f2c( (t): """ >>> f2c(212) 100 >>> f2c(32) 0 >>> g2c(-40) -40 >>> g2c(37) 3 """ # Zde napište svou fci
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |