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 try
– except
:
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 try
– except
:
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