Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.
Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.

17. diel - Funkcie a výnimky v Pythone

V minulej lekcii, Cykly v Pythone druhýkrát - Výraz pass, break a continue , sme ucelili naše znalosti cyklov ďalšími konštrukciami a kľúčovými slovami pass, break a continue.

V dnešnom Python tutoriále sa naučíme funkcionálny štýl programovania. Ďalej sa pozrieme na jednoduché ošetrovanie chýb programu a vylepšíme si kalkulačku.

Funkcie

Doteraz sme programy písali imperatívne - urob tento príkaz, následne tamten a tak ďalej. Týmto spôsobom je možné písať jednoduché a krátke programy. Väčšie programy by však boli veľmi neprehľadné. Preto prejdeme na ďalšiu programovaciu paradigmu (spôsob, ako niečo naprogramovať) – na funkcionálne programovanie. Náš program si rozdelíme na menšie časti (podproblémy), ktoré vyriešime samostatne. Jednotlivé problémy riešia funkcie. Pre funkcionálny štýl programovania môžeme použiť všetko, čo sme sa doteraz naučili.

Funkcia obvykle prijíma argumenty (dáta, ktoré funkcia spracuje) a niečo vracia, napríklad výslednú hodnotu. Nemusí ale tiež vracať nič - ako napr. funkcia print().

Základné syntax funkcie

Funkcia sa definuje pomocou kľúčového slova def a výslednú hodnotu vracia kľúčovým slovom return. Za def sa píše medzera a potom názov funkcie. Za názov funkcie sa dávajú jednoduché zátvorky, do ktorých sa píšu názvy jednotlivých argumentov. Argumentom funkcie v programovacom jazyku Python je hodnota alebo premenná, ktorú odovzdávame funkciu, aby hodnotu alebo premennú mohla použiť počas svojho vykonávania. Keď funkciu definujeme, špecifikujeme, aké argumenty môže prijať. Potom, keď funkciu voláme, dosadzujeme hodnoty pre tieto argumenty. Funkcia môže byť aj bez argumentov. Na konci prvého riadku sa píše dvojbodka. Telo funkcie sa štandardne odsadzuje:

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

Takto vytvorená funkcia mocnina() vracia číslo umocnené na druhú. Napríklad pri zavolaní mocnina(1) vráti 1, pri zavolaní mocnina(2) vráti 4 atď. V praxi to vyzerá nasledovne:

def mocnina(cislo):         # Cislo je název argumentu
    cislo = cislo ** 2
    return cislo

prvni_cislo = mocnina(2)    # Dvojka v závorce je námi dosazovaná hodnota argumentu
print(prvni_cislo)

Argumentov môže byť samozrejme aj viac:

def soucin(prvni_cislo, druhe_cislo, treti_cislo):
    cislo = prvni_cislo * druhe_cislo * treti_cislo
    return cislo

priklad = soucin(2, 3, 4)
print(priklad)

Funkciu môžeme tiež použiť ako argument inej funkcie:

def soucin(prvni_cislo, druhe_cislo, treti_cislo):
    cislo = prvni_cislo * druhe_cislo * treti_cislo
    return cislo

print(soucin(2, 3, 4))  #  Naši funkci používáme jako argument funkce print()

Druhy argumentov funkcie

V Pythone máme dva druhy argumentov:

  • pozičné,
  • kľúčové.
Pozičné argumenty

Pozičné argumenty sme videli vyššie. Na ich pozícii sa dosadí argument na rovnakej pozícii pri volaní funkcie. Tieto argumenty nemajú danú základnú hodnotu. Ich syntax je jednoduchá: nazev_argumentu.

Kľúčové argumenty

Kľúčové argumenty majú už vopred nastavenú hodnotu, ktorú je možné zmeniť. Na rozdiel od argumentov pozičných sú tie kľúčové označené kľúčovým slovom (majú svoj názov). Tieto argumenty nemusia byť pri volaní funkcie zadané v poradí, v ktorom sú deklarované. Navyše ich nemusíme pri volaní funkcie inicializovať (určiť ich hodnotu). V takom prípade sa použije ich východisková hodnota. Syntax vyzerá takto: nazev_argumentu=hodnota.

Všetky pozičné argumenty musia byť deklarované pred tými kľúčovými!

Ako príklad si ukážme vylepšenú verziu funkcie mocnina:

def mocnina(cislo, exponent=2):    # Cislo je poziční argument, exponent je klíčový argument
    cislo = cislo ** exponent
    return cislo

print(mocnina(2))

Prvý argument je pozičný, druhý je kľúčový. Teraz môžeme volať mocnina(1), mocnina(1, exponent=2), prípadne mocnina(1, 2) a dostaneme rovnaký výsledok. Navyše môžeme umocniť 3 na 4 - mocnina(3, exponent=4). To už však musíme hodnotu exponentu špecifikovať:

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

print(mocnina(3, exponent=4))

Všeobecne sa pozičné argumenty zapisujú ako args a kľúčové ako kwargs.

Zvláštnou vlastnosťou Pythona je to, že kľúčový argument (kwarg) môžeme zadávať aj ako obyčajný argument (arg), ak jeho poradie zodpovedá deklarácii funkcie.

Ako príklad si ešte raz ukážme našu funkciu mocnina():

def mocnina(cislo, exponent=2):    # Cislo je poziční argument, exponent je klíčový argument
    cislo = cislo ** exponent
    return cislo

print(mocnina(3, 4))               # Zde jsme zavolali klíčový argument 'exponent' jako běžný poziční argument

Operátor *

V Pythone operátor * neslúži len pre násobenie. Keď ho použijeme v definícii funkcie, umožňuje nám zachytávať premenný počet pozičných argumentov. Týmto spôsobom môžeme flexibilne pracovať s rôznym množstvom vstupných hodnôt. Uveďme si príklad:

def nejaka_funkce(*pozicni_argumenty):
    pass

V tomto prípade sa všetky zadané pozičné argumenty odovzdané funkcii zhromaždia do n-tice (dátový typ podobný zoznamu) s názvom pozicni_argumenty. S touto n-ticou môžeme ďalej pracovať, napríklad ju prechádzať pomocou cyklu for:

def soucin(*cisla):
    vysledek = 1
    for cislo in cisla:
        vysledek = vysledek * cislo
    return vysledek

print(soucin(2, 3, 4, 5))

Keď teda zavoláme funkciu soucin(2, 3, 4, 5), všetky štyri čísla sú zbalené do n-tice cisla av tele funkcie sú následne prístupné ako cisla[0], cisla[1], cisla[2] a cisla[3]. N-tici potom jednoducho prejdeme pomocou cyklu for. Vďaka tejto vlastnosti môžeme funkciu soucin() volať s ľubovoľným počtom čísel.

Keď použijeme * v definícii funkcie bez nasledujúceho mena premennej, hovoríme tým, že funkcia smie od tohto miesta ďalej prijímať iba kľúčové argumenty. Inými slovami, všetky argumenty za * musia byť pri volaní funkcie špecifikované ako kľúčové.

Ukážme si to na konkrétnom príklade:

def moje_funkce(*, prvni_klicovy_arg=1, druhy_klicovy_arg=1):
    print(prvni_klicovy_arg, druhy_klicovy_arg)

V tomto prípade nám funkcia nedovolí použiť pozičné argumenty. Musíme ich špecifikovať ako kľúčové argumenty:

moje_funkce(prvni_klicovy_arg=5, druhy_klicovy_arg=10)  # výstup: 5 10

# Následující volání by vyvolalo chybu:
# moje_funkce(5, 10)   # TypeError: moje_funkce() takes 0 positional arguments but 2 were given

Ak chceme použiť zápis s operátorom * pre kľúčové argumenty, napíšeme dve hviezdičky.

Rekurzia

Pojem rekurzia označuje zápis kódu, kedy funkcia volá samu seba. Rekurziu môžeme použiť napríklad na výpočet faktoriálu. Uveďme si príklad:

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

Pri rekurzii si musíme dať pozor, aby sa funkcia niekedy ukončila. Inak program spadne kvôli pretečeniu zásobníka. Rekurzie podrobne vysvetľujeme pri algoritme faktoriálu.

Typovanie funkcií

Pokiaľ chceme explicitne deklarovať typ parametrov funkcií a ich návratové hodnoty, použijeme na to operátor ->. Pozrime sa na príklad:

def generuj_ahoj(jmeno: str) -> str:      #  Návratová hodnota funkce bude str
    return "Ahoj, " + jmeno + "!"

V rozsiahlejších projektoch je typovanie obzvlášť užitočné pre udržanie kvality a konzistencie kódu. Uľahčuje spoluprácu viacerých vývojárov a pomáha pri zachovaní dobrej architektúry.

Mimochodom, podobne postupujeme aj u premenných. Len operátor je iný. Pokiaľ chceme explicitne deklarovať typ premennej, použijeme na to dvojbodkový operátor ::

text: str = "Ahoj světe!"

Teraz je jasne povedané, že text je typu str. V tomto prípade to možno spoznať podľa hodnoty "Ahoj světe!", Takže je tu také označenie trochu nadbytočné. Avšak v programoch v praxi býva veľa miest, kde to už tak jasné nie je. Potom má deklarovanie typu zmysel.

Úprava výstupu funkcie print()

Teraz sa naučíme upraviť si funkciu print(). Už sme sa s tým čiastočne stretli v predchádzajúcich lekciách, keď sme upravovali, čím funkcia print() ukončí riadok. Teraz sa na ňu pozrieme podrobnejšie. Najprv sa zameriame na jej dva kľúčové argumenty:

  • sep – Argument udáva medzery medzi jednotlivými prvkami (pozičnými argumentmi). Normálne je nastavený na medzeru (" ").
  • end – Tento argument definuje, čím sa zápis ukončí. Normálne sa tak deje znakom nového riadka ("\n").
Poďme si to vyskúšať na príklade:
print(1, 2, 3, "a", sep="-")

print("Žiadny nový riadok", end=" ")
print("nebude.", end=" ")

Ošetrenie chýb

Vo väčšine nami doteraz vytvorených programov sme užívateľovi umožnili spáchať chybu pri číselnom vstupe. Pokiaľ by teda užívateľ zadal namiesto čísla napr. písmeno, náš program by spadol.

Teraz si teda ukážeme, ako takéto chyby ošetriť. Ošetrenie sa vykonáva pomocou bloku tryexcept:

try:
    #  Blok príkazov
except jmeno_prvni_vyjimky:
    #  Blok príkazov
except jmeno_dalsi_vyjimky:
    #  Blok príkazov
else:
    #  Tento blok príkazov sa vykoná, pokiaľ nenastane žiadna chyba.
finally:
    #  Blok príkazov, ktorý sa vykoná vždy.

Ak chceme zachytiť aj chybovú správu, musíme kód upraviť takto:

except jmeno_vyjimky as chyba:
    #  Text výnimky sa uloží do premennej chyba.

Chybám sa v Pythone (av objektových jazykoch všeobecne) hovorí výnimky. Tie základné sú nasledujúce:

  • SyntaxError – chyba je v zdrojovom kóde,
  • ZeroDivisionError – pokus o delenie nulou,
  • TypeError – nesprávne použitie dátových typov, napr. sčítanie reťazca a čísla,
  • ValueError – nesprávna hodnota.
Všetky výnimky nájdeme v dokumentácii Pythona.

Ukážme si jednoduchý príklad, ktorý využije všetky kľúčové slová bloku tryexcept:

while True:
    try:
        delenec = float(input("Zadajte číslo, ktoré chcete deliť: "))
        delitel = float(input("Zadajte číslo, ktorým chcete deliť: "))

        vysledek = delenec / delitel

    except ZeroDivisionError:
        print("Delenie nulou nie je možné!")

    except ValueError:
        print("Bola zadaná neplatná hodnota!")

    else:
        print(f"Výsledok delenia je: {vysledek}")

    finally:
        opustit = input("Chcete ukončiť program? (áno/nie): ").lower()
        if opustit == "áno":
            break

Kód si vysvetlíme. V tomto kóde sa program užívateľa spýta, ktoré čísla chce deliť. Pokiaľ užívateľ skúsi deliť nulou, program zachytí a vyhodí chybovú správu. Rovnako tak dôjde k zachyteniu chyby v prípade, že používateľ vloží niečo iné ako číslo. Blok else sme použili na výpis výsledku delenia v prípade, že nedôjde k výnimke. Blok finally potom umožňuje užívateľovi opustiť nekonečný cyklus a ukončiť program.

Vylepšenie kalkulačky

Teraz máme dosť znalostí na to, aby sme mohli znova vylepšiť našu kalkulačku. Najprv si pripomeňme jej doterajší kód:

print("Vitajte v kalkulačke")
pokracovat = "áno"
while pokracovat == "áno":
    a = float(input("Zadajte prvé číslo: "))
    b = float(input("Zadajte druhé číslo: "))
    print("Zvoľte si operáciu:")
    print("1 - sčítanie")
    print("2 - odčítanie")
    print("3 - násobenie")
    print("4 - delenie")
    volba = int(input())
    vysledek = 0.0
    match volba:
        case 1:
            vysledek = a + b
        case 2:
            vysledek = a - b
        case 3:
            vysledek = a * b
        case 4:
            if b != 0:
                vysledek = a / b
            else:
                print("Nulou nemožno deliť!")
                vysledek = "N/A"

    if 0 < volba < 5:
        print(f"Výsledok: {vysledek}")
    else:
        print("Neplatná voľba")
    nezadano = True
    while nezadano:
        odpoved = input("\nPrajete si zadať ďalší príklad? ano / ne: ").lower()
        if odpoved == "áno":
            nezadano = False
        elif odpoved == "nie":
            nezadano = False
            pokracovat = False
        else:
            pass
print("Ďakujem za použitie kalkulačky, aplikáciu ukončíte ľubovoľným klávesom.")

Pusťme sa do vylepšovania kódu. Najprv naprogramujeme "užívateľovi vzdornú" funkciu na získanie čísla zo vstupu:

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 sa tak dlho opakuje v cykle, kým od užívateľa nezíska správny vstup. Riadok s float() prevedie reťazec na desatinné číslo.

Ďalej si naprogramujeme funkciu, ktorá umožní zadať iba ano a ne:

def dalsi_priklad():
    while True:
        odpoved = input("\nPrajete si zadať ďalší príklad? ano / ne: ").lower()
        if odpoved == "áno":
            return True
        elif odpoved == "nie":
            return False
        else:
            print("Prosím, odpovedzte &#39;áno&#39; alebo &#39;nie&#39;.")

Ďalej musíme ošetriť, čo sa stane po voľbe operácie. Užívateľ by mohol zadať napríklad číslo mimo intervalu <1;4> a tým zvoliť neexistujúcu operáciu. Prípadne zadať čísla 5 a 0 a potom zvoliť delenie. Náš program by potom skončil s výnimkou ZeroDivisionError. Musíme sa teda o tieto potenciálne chyby postarať. Je tiež nevhodné, aby funkcia tlačila výsledok, funkcia má vždy vracať hodnotu:

def proved_operaci(a, b):
    while True:
        print("1 - sčítanie")
        print("2 - odčítanie")
        print("3 - násobenie")
        print("4 - delenie")

        try:
            vstup = int(input("Zadajte číslo pre vybranú operáciu: "))

            match vstup:
                case 1:
                    return a + b
                case 2:
                    return a - b
                case 3:
                    return a * b
                case 4:
                    if b != 0:
                        return a / b
                    else:
                        print("Nulou nemožno deliť!")
                        continue  # Pokračování ve smyčce a nový vstup od uživatele
                                  # Použití continue zde už není nutné, jen dáváme najevo, že pokračujeme další iterací smyčky
                case _:
                    print("Neplatná voľba. Zadajte číslo zodpovedajúce vybranej operácii.")
                    continue  # Pokračování ve smyčce a nový vstup od uživatele
                              # Použití continue zde už není nutné, jen dáváme najevo, že pokračujeme další iterací smyčky

        except ValueError:
            print("Neplatný vstup. Zadajte prosím číslo.")
            continue  # Pokračování ve smyčce a nový vstup od uživatele
                      # Použití continue zde už není nutné, jen dáváme najevo, že pokračujeme další iterací smyčky

Hlavný cyklus programu sa s našimi novými funkciami značne zjednoduší:

print("Kalkulačka\n")
pokracovat = True

while pokracovat:
    prvni_cislo = nacti_cislo("Zadaj číslo: ", "Neplatné číslo!\n")
    druhe_cislo = nacti_cislo("Zadaj číslo: ", "Neplatné číslo!\n")
    vysledek_vypoctu = proved_operaci(prvni_cislo, druhe_cislo)
    print(f"Výsledok: {vysledek_vypoctu}")
    pokracovat = dalsi_priklad()

print("Ďakujem za použitie kalkulačky, aplikáciu ukončíte klávesom Enter.")
input()

Celý kód sme rozdelili do prehľadných a jednoduchých blokov. Prípadné úpravy v kóde potom budú výrazne jednoduchšie. Celý kód je tiež oveľa čitateľnejší. Toto je podstata funkcionálnej paradigmy !

V budúcej lekcii, Matematické funkcie v Pythone a knižnica math , sa pozrieme na užitočné matematické funkcie.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 1555x (1.19 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python

 

Predchádzajúci článok
Cykly v Pythone druhýkrát - Výraz pass, break a continue
Všetky články v sekcii
Základné konštrukcie jazyka Python
Preskočiť článok
(neodporúčame)
Matematické funkcie v Pythone a knižnica math
Článok pre vás napísal gcx11
Avatar
Užívateľské hodnotenie:
7 hlasov
(^_^)
Aktivity