IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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 power(number):
    number = number ** 2
    return number

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

def power(number):         # number is the argument name
    number = number ** 2
    return number

first_number = power(2)    # 2 is the value passed to the function
print(first_number)

Argumentov môže byť samozrejme aj viac:

def product(first_number, second_number, third_number):
    number = first_number * second_number * third_number
    return number

example = product(2, 3, 4)
print(example)

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

def product(first_number, second_number, third_number):
    number = first_number * second_number * third_number
    return number

print(product(2, 3, 4))  #  we use our function as an argument of the print() function

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á: argument_name.

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: argument_name=value.

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

Ako príklad si ukážme vylepšenú verziu funkcie na výpočet mocniny:

def power(base, exponent=2):    # base is a positional argument, exponent is a keyword argument
    result = base ** exponent
    return result

print(power(2))

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

def power(base, exponent=2):
    result = base ** exponent
    return result

print(power(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 power():

def power(base, exponent=2):    # base is a positional argument, exponent is a keyword argument
    result = base ** exponent
    return result

print(power(3, 4))               # here we called the keyword argument exponent as a regular positional 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 some_function(*positional_arguments):
    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 positional_arguments. S touto n-ticou môžeme ďalej pracovať, napríklad ju prechádzať pomocou cyklu for:

def product(*numbers):
    result = 1
    for number in numbers:
        result = result * number
    return result

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

Keď teda zavoláme funkciu product(2, 3, 4, 5), všetky štyri čísla sú zbalené do n-tice numbers a v tele funkcie sú následne prístupné ako numbers[0], numbers[1], numbers[2] a numbers[3]. N-tici potom jednoducho prejdeme pomocou cyklu for. Vďaka tejto vlastnosti môžeme funkciu product() 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 my_function(*, first_keyword_arg=1, second_keyword_arg=1):
    print(first_keyword_arg, second_keyword_arg)

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

my_function(first_keyword_arg=5, second_keyword_arg=10)  # output: 5 10

# The following call would raise an error:
# my_function(5, 10)   # TypeError: my_function() 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 factorial(number):
    if number > 0:
        return factorial(number - 1) * number
    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 generate_hello(name: str) -> str:      #  the return value of the function will be str
    return "Hello, " + name + "!"

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 = "Hello World!"

Teraz je jasne povedané, že text je typu str. V tomto prípade to možno spoznať podľa hodnoty "Hello World!", 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("No new line", end=" ")
print("will appear", 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:
    #  Block of commands
except first_exception_name:
    #  Block of commands
except another_exception_name:
    #  Block of commands
else:
    #  This block of commands will execute if no errors occur.
finally:
    #  Block of commands that will always execute.

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

except exception_name as error:
    #  The exception text is stored in the variable 'error'.

Chybám sa v Pythone (a v 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:

go_on = True
while go_on:
    try:
        dividend = float(input("Enter the number you want to divide:  "))
        divisor  = float(input("Enter the number you want to divide by:  "))

        result = dividend / divisor

    except ZeroDivisionError:
        print("Division by zero is not allowed!")

    except ValueError:
        print("An invalid value was entered!")

    else:
        print(f"The result of the division is: {result}")

    finally:
        leave = input("Do you want to exit the program? (yes/no):  ").lower()
        if leave == "yes":
            go_on = False

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ť 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("Welcome to our calculator/T}")
go_on = "yes"
while go_on == "yes":
    a = float(input("Enter first number:  "))
    b = float(input("Enter second number:  "))
    print("Choose one of the following operations: ")
    print("1 - addition")
    print("2 - subtraction")
    print("3 - multiplication")
    print("4 - division")
    option = int(input())
    result = 0.0
    match option:
        case 1:
            result = a + b
        case 2:
            result = a - b
        case 3:
            result = a * b
        case 4:
            if b != 0:
                result = a / b
            else:
                print("Division by zero is not allowed!")
                result = "N/A"

    if 0 < option < 5:
        print(f"Result: {result}")
    else:
        print("Invalid option")
    go_on = input("Would you like to make another calculation? [yes/no]: ")
print("Thank you for using our calculator.")

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

def get_number(prompt_text, error_text):
    invalid_input = True
    while invalid_input:
        try:
            number = float(input(prompt_text))
            invalid_input = False
        except ValueError:
            print(error_text)

    return number

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 yes a no:

def another_example():
    invalid_response = True
    while invalid_response:
        answer = input("Would you like to make another calculation? [yes/no]: ").lower()
        if answer in ["yes", "no"]:
            invalid_response = False
        else:
            print("Please answer 'yes' or 'no'.")
    return answer == "yes"  #  The function returns True when the string "yes" is stored in the answer variable, otherwise it returns False.

Ďalej musíme ošetriť, čo sa stane po voľbe operácie. Užívateľ by mohol zadať napríklad číslo mimo interval <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 perform_operation(a, b):
    operation = 0
    while operation not in [1, 2, 3, 4]:
        print("1 - addition")
        print("2 - subtraction")
        print("3 - multiplication")
        print("4 - division")

        #  To avoid handling user input again when selecting an operation, we'll use our already created function here
        #  We'll cast its output (of type float) to int
        operation = int(get_number("Choose one of the following operations:  ", "Invalid input. Please enter a number..."))

        if operation == 4 and b == 0:
            print("Division by zero is not allowed!")
            operation = 0  #  Reset operation due to division by zero

        elif operation < 1 or operation > 4:
            print("Invalid choice. Please enter a number corresponding to the selected operation.") #  Invalid input handling

    match operation:
        case 1:
            return a + b
        case 2:
            return a - b
        case 3:
            return a * b
        case 4:
            return a / b

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

print("Calculator\n")
go_on = True

while go_on:
    first_number = get_number("Enter first number: ", "Invalid number!\n")
    second_number = get_number("Enter second number: ", "Invalid number!\n")
    calculation_result = perform_operation(first_number, second_number)
    print(f"Result: {calculation_result}")
    go_on = another_example()

print("Thank you for using our calculator.")
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é 0x (1.37 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štrukcia 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