8. díl - Funkce a výjimky v Pythonu

Python Základní konstrukce Funkce a výjimky v Pythonu

V minulém tutoriálu o programování v Pythonu jsme si ukázali metody na textových řetězcích. V tomto tutoriálu se naučíme používat funkce a funkcionální styl programování. Dále se naučíme jednoduché ošetřování chyb programu. Vylepšíme si naši kalkulačku a uděláme z ní první chybám odolný program.

Funkce

Doteď jsme programy psali imperativně - proveď tento příkaz, potom tamten atd. Tímto způsobem lze psát jednoduché a krátké programy. Větší programy by však byly velmi nepřehledné. Proto přejdeme na další programovací paradigma (způsob jak něco naprogramovat) - funkcionální styl programování. Náš program si rozdělíme na menší části (podproblémy), které řešíme samostatně. Jednotlivé podproblémy řeší funkce. Pro funkcionální styl programování můžeme použít vše, co jsme se naučili doposud.

Funkce obvykle přijímá argumenty (data, která zpracuje) a něco vrací - nějakou výslednou hodnotu apod. (ale taky nemusí vracet nic jako např. funkce print()).

Základní syntaxe funkce

Funkce mají syntaxi podobnou větvícím příkazům (if, elif, else). Funkce se definuje pomocí klíčového slova def a výslednou hodnotu vrací klíčovým slovem return. Za def se píše mezera a poté název funkce. Za název funkce se dávají jednoduché závorky, do kterých se píší názvy jednotlivých argumentů. Funkce může být i bez argumentů. Při volání funkce se do nich dosazují hodnoty argumentů. Na konci prvního řádku se píše dvojtečka. Tělo funkce se standardně odsazuje.

Příklad:

def mocnina(cislo):
    cislo = cislo ** 2
    return cislo

Takto vytvořená funkce mocnina vrací číslo umocněné na druhou. Například při zavolání mocnina(1) vrátí 1, při zavolání mocnina(2) vrátí 4 atd.

V praxi:

>>> def mocnina(cislo):
...     cislo = cislo ** 2
...     return cislo
>>> prvni_cislo = mocnina(2)
>>> prvni_cislo
4

Argumentů samozřejmě může být i více např:

>>> def soucin(prvni_cislo, druhe_cislo, treti_cislo):
...     cislo = prvni_cislo * druhe_cislo * treti_cislo
...     return cislo
>>> priklad = soucin(2, 3, 4)
>>> priklad
24

Argumenty

Argumentů jsou dva druhy - poziční a klíčové.

Poziční argumenty jsme viděli výše. Na jejich pozici se dosadí argument na stejné pozici při volání funkce. Tyto argumenty nemají dánu základní hodnotu.

Syntaxe: název_argumentu

Klíčové argumenty mají již předem nastavenou hodnotu, kterou lze změnit. Na rozdíl od argumentů pozičních jsou označeny klíčovým slovem (mají svůj název). Tyto argumenty mají dánu výchozí hodnotu a nemusí být při volání funkce zadány v tom pořadí, v jakém jsou deklarovány. Navíc je nemusíme při volání funkce inicializovat (určit jejich hodnotu), v tom případě se použije je základní hodnota.

Syntaxe: název_argumen­tu=hodnota

Všechny poziční argumenty musí být deklarovány před klíčovými!

Nyní příklad - vylepšená verze funkce mocnina:

def mocnina(cislo, exponent=2):
    cislo = cislo ** exponent
    return cislo

První argument je poziční, druhý je klíčový. Nyní můžeme volat mocnina(1) nebo mocnina(1, exponent=2), a dostaneme stejný výsledek. Navíc třeba můžeme umocnit 3 na 4 - mocnina(3, exponent=4).

Operátor *

Můžeme použít i operátor *. Ten nám umožní dělat zajímavější věci.

Např.:

nejaka_funkce(*pozicni_argumenty)

Obecně se poziční argumenty zapisují jako args a klíčové argumenty jako kwargs.

Díky tomuto zápisu se všechny zadané poziční argumenty vloží do n-tice (něco jako seznam) s názvem pozicni_argumenty. Tu poté můžeme například projíždět cyklem for.

Chceme-li to samé pro klíčové argumenty napíšeme dvě hvězdičky.

Příklad:

>>> def soucin(*cisla):
...     vysledek = 1
...     for cislo in cisla:
...         vysledek = vysledek * cislo
...     return vysledek
>>> priklad = soucin(2, 3, 4, 5)
>>> priklad
120

Pro zájemce - můžeme použít chuťovku typu:

funkce(*, prvni_klicovy_arg=1, druhy_klicovy_arg=1)

Přičemž nám funkce nedovolí použít poziční argumenty.

Rekurze

Rekurze označuje když funkce volá sama sebe. Lze ji použít například na výpočet faktoriálu.

Příklad:

def faktorial(cislo):
    if cislo > 0:
        return faktorial(cislo - 1) * cislo
    else:
        return 1

Při rekurzi si musíte dát pozor, aby se někdy ukončila. Jinak program upadne na přetečení zásobníku. Rekurze je pro zájemce podrobně vysvětlena u algoritmu faktoriálu.

print()

Jak jsem kdysi slíbil, tak se naučíme upravit si funkci print. Nyní se podíváme na její dva klíčové argumenty:

sep - mezery mezi jednotlivými prvky (pozičními argumenty) - normálně nastaveno na mezeru (" ")

end - čím se zápis ukončí - normálně znak nového řádku ("\n")

Příklady:

>>> print(1, 2, 3, "a", sep="-")
1-2-3-a
>>> print("Žádná nová řádka!", end=" ")
Žádná nová řádka! >>>

Ošetření chyb

Ve většině programů, co jsme dosud vytvořili, byla chyba při vstupu čísla. Pokud jsme zadali místo čísla např. písmeno, tak program spadl.

Nyní si ukážeme, jak takovéto chyby ošetřit. Ošetření se provádí pomocí bloku try, except.

Syntaxe:

try:
   #blok příkazů
except jmeno_prvni_vyjimky:
   #blok příkazů
except jmeno_dalsi_vyjimky:
   #blok příkazů
#zde je buď konec, nebo zachycení dalších výjimek

Pokud chceme zachytit i chybovou zprávu - musíme napsat:

except jmeno_vyjimky as chyba:
    #text vyjímky se uloží do proměnné chyba

Chybám se v Pythonu (a v objektových jazycích obecně) říká výjimky. Ty základní jsou následující:

  • SyntaxError - chyba ve zdrojovém kódu
  • ZeroDivisionError - dělení nulou
  • TypeError - nesprávné použití datových typů - např. sčítání řetězce a čísla apod.
  • ValueError - nesprávná hodnota

Více výjimek zde (AJ): https://docs.python.org/…eptions.html

A nyní si ještě jednou vylepšíme naši kalkulačku:

print("Kalkulačka\n")
pokracovat = True
while pokracovat:
    prvni_cislo = int(input("Zadejte první číslo: "))
    druhe_cislo = int(input("Zadejte druhé číslo: "))
    print("1 - sčítání")
    print("2 - odčítání")
    print("3 - násobení")
    print("4 - dělení")
    print("5 - umocňování")
    cislo_operace = int(input("Zadejte číslo operace: "))
    if cislo_operace == 1:
        print("Jejich součet je:", prvni_cislo + druhe_cislo)
    elif cislo_operace == 2:
        print("Jejich rozdíl je:", prvni_cislo - druhe_cislo)
    elif cislo_operace == 3:
        print("Jejich součin je:", prvni_cislo * druhe_cislo)
    elif cislo_operace == 4:
        print("Jejich podíl je:", prvni_cislo / druhe_cislo)
    elif cislo_operace == 5:
        print(prvni_cislo, "na", druhe_cislo, "je:", prvni_cislo ** druhe_cislo)
    else:
        print("Neplatná volba!")
    nezadano = True
    while nezadano:
        odpoved = input("\nPřejete si zadat další příklad? y / n: ")
        if (odpoved == "y" or odpoved == "Y"):
            nezadano = False
        elif (odpoved == "n" or odpoved == "n"):
            nezadano = False
            pokracovat = False
        else:
            pass
input("\nStiskněte libovolnou klávesu...")

Nejprve vyrobíme funkci na získání čísla ze vstupu od uživatele:

def nacti_cislo(text_zadani, text_chyba):
    spatne = True
    while spatne:
        try:
            cislo = float(input(text_zadani))
            spatne = False
        except ValueError:
            print(text_chyba)
    else:
        return cislo

Program se opakuje v cyklu, dokud nezadáme správný vstup. Řádek s float() převede řetězec na desetinné číslo.

Dále vyrobíme funkci na vstup "y" nebo "n":

def dalsi_priklad():
    nezadano = True
    while nezadano:
        odpoved = input("\nPřejete si zadat další příklad? y / n: ")
        if (odpoved == "y" or odpoved == "Y"):
            return True
        elif (odpoved == "n" or odpoved == "N"):
            return False
        else:
            pass

Skoro to samé...

Další na řadě je volba:

def volba(prvni_cislo, druhe_cislo):
    print("1 - sčítání")
    print("2 - odčítání")
    print("3 - násobení")
    print("4 - dělení")
    print("5 - umocňování")
    cislo_operace = nacti_cislo("Zadej volbu: ", "Neplatné zadání!\n")
    if cislo_operace == 1:
        print("Jejich součet je:", prvni_cislo + druhe_cislo)
    elif cislo_operace == 2:
        print("Jejich rozdíl je:", prvni_cislo - druhe_cislo)
    elif cislo_operace == 3:
        print("Jejich součin je:", prvni_cislo * druhe_cislo)
    elif cislo_operace == 4:
        mezivysledek = 0
        try:
            mezivysledek = prvni_cislo / druhe_cislo
            print("Jejich podíl je:", mezivysledek)
        except ZeroDivisionError:
            print("Dělení nulou!")
    elif cislo_operace == 5:
        print(prvni_cislo, "na", druhe_cislo, "je:", prvni_cislo ** druhe_cislo)
    else:
        print("Neplatná volba!")

A ošetření dělení nulou. Šlo by i jen kontrolovat, zdali je druhé číslo 0.

A nyní hlavní cyklus:

def main():
    print("Kalkulačka\n")
    pokracovat = True
    while pokracovat:
        prvni_cislo = nacti_cislo("Zadej číslo: ", "Neplatné číslo!\n")
        druhe_cislo = nacti_cislo("Zadej číslo: ", "Neplatné číslo!\n")
        volba(prvni_cislo, druhe_cislo)
        if dalsi_priklad():
            pass
        else:
            pokracovat = False
    input("\nStiskněte libovolnou klávesu...")
main()

Kompletní kód kalkulačky je k dispozici níže ke stažení.

Příště se podíváme na import knihoven a několik si jich představíme. A ukážeme si pythonovská easter eggs :)


 

Stáhnout

Staženo 174x (1.96 kB)
Aplikace je včetně zdrojových kódů v jazyce python

 

  Aktivity (1)

Článek pro vás napsal gcx11
Avatar
(^_^)

Jak se ti líbí článek?
Celkem (9 hlasů) :
4.777784.777784.777784.777784.77778


 


Miniatura
Předchozí článek
Cvičení k 6. a 7. lekci Pythonu
Miniatura
Všechny články v sekci
Základní konstrukce jazyka Python
Miniatura
Následující článek
Vícerozměrná pole v Pythonu

 

 

Komentáře
Zobrazit starší komentáře (10)

Avatar
Milan Šmídl:

Zdar chlapy! jak postupuje pc pri ctení kodu té rekurze a jaké jsou instrukce pro aritmetickou jednotku? Nikde nevidim blok příkazu a když čtu instrukce jak jdou po sobe, tak se mi nikde neuzavře vypočet? funkce volá sama na sebe to sice ano, je tam (cislo -1), to ano ale jako argument funkce a ja proste nikde nevidim blok prikazu a jsem z toho už jantar:) jako vidim o co tady jde to ano, ale když jdu krok po kroku, tak jsem v pasti..kdyby mi toto dokázal někdo vysvětlit, tak opravdu veliký respekt

 
Odpovědět 30.12.2015 15:33
Avatar
Taskkill
Redaktor
Avatar
Odpovídá na Milan Šmídl
Taskkill:

Nejsem sice pythonar, jen c-like jazykar, ale zkusim to: zavolas faktorial(5) -> vleze se do funkce, vyhodnoti podminky, a zjisti se ze 5 je vetsi nez jedna, funkce se rozhodne vratit hodnotu, ktera je specifikovana jako navratova hodnota volani faktorial(4), coz vede k pozastaveni prvniho faktorialu a spusteni druheho, to vede k volani tretiho na cislo 3, ctvrteho na cislo 2 a posledniho pateho na cislo jedna, toto volani vrati jednicku, jednicka se tedy vynasobi se dvojkou o uroven vejs a vratej se, o dalsi uroven vejs se vynasobi se trojkou a vrati se o dalsi uroven vejs, tak dal az uplne nahoru. To co provadi pocitat se naziva rekurzivni volani, nebo proste rekurze, to co k tomu potrebuje je zasobnik, neco (pamet), najeky misto kde se to vsechno rozpracovany ulozi a zavola se dalsi, nizsi vrstva, ve sve podstate si to predstav jako pohadku o slepicce a kohoutkovi, (tenhe priklad se casto uvadi) slepicka sice potrebovala vodu pro toho hlupaka nenazranyho kohouta, ale aby dostala vodu musela nekomu kdo ma vodu prinest (nevimco treba drahe kameni) ale ten kdo mel drahe kameni chtel prinest (nevimco treba sportak) ale ten kdo prodaval sportaky tajne snil o (nevimcem treba o modelce z Milana) ale .... atd...slepicka nejdriv zjistila kerej ukol bude ten posledni, a kdyz se ji podarilo ho splnit, probublala se s vysledkama az uplne nahoru k trose vody. To je pointa, a taky, ze kdyby slepicka chodila nakupovat do coopu, nebo do jednoty, nemusela by tak jancit a vsem plnit zivotni sny. Vysvetleno? Pochopeno? :)

 
Odpovědět 30.12.2015 16:12
Avatar
Odpovídá na Taskkill
Milan Šmídl:

Jasně jasně, suprový vyklad..no a jak teda vypada ten pametovy regist? V tomto pripade se nevraci aritmetická hodnota, ale funkce krát aritmetická hodnota...(v registru tedy bude f5,f4, f*3, f*2,...)no ale když by ta rekurze byla už definována přímo v jazyce Python tak by to fungovalo..musim si to jeste nakreslit:) každopadně moc pěkny vyklad a já ti za něj mockrát děkuji
Dam sem kod který jsem se snazil aplikovat při tvorbě jednoducheho fraktalu, vpodstatě dvě fu nkce volajici jedna na druhou a předavajicí si argumenty..pak mi Coells mistr pythonař poradil rekurzy a ja si vzpoměl, že to je to na co jsem se vyprdnul u funkci, tak jsem se musel vratit:)..měj se a ještě jednou diky

Editováno 30.12.2015 18:37
 
Odpovědět 30.12.2015 18:35
Avatar
hanpari
Redaktor
Avatar
Odpovídá na Milan Šmídl
hanpari:

Jednoduchou rekurzi si můžeš odkrokovat tady:

http://tinyurl.com/jefnb7c

 
Odpovědět  ±0 30.12.2015 20:35
Avatar
Taskkill
Redaktor
Avatar
Odpovídá na Milan Šmídl
Taskkill:

Nemas vubec za co.
Jinak, kdyz mas dve funkce, ktery na sebe volaji v kruhu, tak je to stale rekurze, jen neprima. Nic to ale nemeni na chovani. To co se ptas, jestli to chapu spravne, tak se ptas na to co se vlastne vraci, a ja se domnivam (pokud jsem pochopil vyklad naseho prednasejiciho spravne), ze je skutecne ciselna hodnota, prave proto, ze se to vsechno vyhodnoti postupne. Nejdriv se zjisti ze se bude vracet navratova hodnota funkce krat cislo...ale nevim jak v pythonu, v cecku treba se stadardne funkce nevraci, to by na tebe kompilator koukal jako na cvoka :D ... misto toho se prave na zasobniku tahle situace vyresi tak, ze se prvni funkce pozastavi DOKUD nebude ta druha (kterou JAKOBY vracis) mit pripravenou hodnotu a nebude ji vracet a takhle dal a dal a hloubs a hloubs. Zajima me, proc vlastne potrebujes znat vzhled pametoveho registru? Ne ze by mi to prislo zvlastni, ale zajima me to.

 
Odpovědět 30.12.2015 21:01
Avatar
Odpovídá na hanpari
Milan Šmídl:

Bože Hanpari prosimtě promiň já jsem ti dal zcela omylem palec dolů a těď to nejde vzít zpět, tak prosimtě to byl jen omyl, omlouvám se:) Každopádně ještě jednou díky chlapy

 
Odpovědět  +1 30.12.2015 23:11
Avatar
Odpovídá na Taskkill
Milan Šmídl:

Jsem technik a mám rád, když už se do něčeho pustím, když si vše dovedu představit co možná nejkomplexněji:) díky za výklad, opravdu si toho moc vážím..

 
Odpovědět 30.12.2015 23:16
Avatar
Taskkill
Redaktor
Avatar
Odpovídá na Milan Šmídl
Taskkill:

Tu komplexnost chapu, mam to podobne, v ten pripad si ale rikam, ze by pro tebe byly jazyky jako Assembler nebo C mnohem obraznejsi. Kdo nevidel pole a zejmena stringy v cecku jako by ani nemohl docenit tu krasu. :)

Jinak nemas vubec za co. Rekurze je moje oblibene tema, tak se k nemu vzdycky rad vyjadrim.

 
Odpovědět 30.12.2015 23:54
Avatar
Odpovídá na hanpari
Milan Šmídl:

Ta funkce visualize code execution je opravdu extremně užitečná...škoda jen, že nemám více času si s tím pohrát..díky za výborný tip

 
Odpovědět 31.12.2015 8:08
Avatar
Odpovídá na Taskkill
Milan Šmídl:

Na assembler se vrhnu jakmile se budu cítit v pythonu jistější, každopádně to mám v plánu a už se na to těším a doufám, že při tom opráším i číslicovou techniku už se mi ty hradla, sčítačky, modula, multiplexory trošku vytratily z hlavy:)..Nemám ambice živit se jako prográmator, takže si to mohu dovolit:) hezký den..

 
Odpovědět 31.12.2015 8:21
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 20. Zobrazit vše