Pokud chceme psát složitější aplikace s dosavadním přístupem bychom se příliš daleko nedostali. Pokud bychom kód psali “od shora dolů” začala by nám postupně exponenciálně růst nepřehlednost a velikost kódu (špagetový kód). Tomuto návrhovému vzoru se říká Imperativní programování.

Proto je vhodné jednotlivé části programu vhodně rozdělit na bloky kódů, které opakovaně používáme. Těmto blokům se říká funkce (případně procedury). Návrhový vzor, který jako jednu z hlavních postupů využívá funkce, se nazývá funkcionální programování.

<aside> <img src="/icons/exclamation-mark_pink.svg" alt="/icons/exclamation-mark_pink.svg" width="40px" />

V literatuře je možné se setkat s pojmem procedura. Procedura je funkce bez návratové hodnoty. Tedy taková funkce, která například pouze něco vypíše na obrazovku, nebo uloží hodnoty do souboru.

</aside>

Základní syntaxe

Funkce se definuje pomocí klíčového slova def, následovaným názvem funkce a jednotlivými parametry. Funkce je ukončena klíčovým slovem return za kterým bývá standardně uveden výsledek funkce.

# Obecná syntaxe:
# def nazevFunkce(parametry)
#   blok příkazů
#   return výsledek
#
# nazevFunkce - identifikátor funkce: jméno funkce, kterým funkci můžeme volat
# parametr - data, které funkce potřebuje

# funkce, která jako výsledek vrací hodnotu součtu jednotlivých parametrů
def soucet(a, b):
    return a + b
    
# volání funkce - je potřeba dodržet pořadí parametrů (poziční parametry)
print(soucet(5,4))

Parametry

Parametry v definici funkce i při jejím volání je možno uvádět několika způsoby:

# parametr s výchozí hodnotou
# pokud tento parametr při volání funkce neuvedeme, je použita výchozí hodnota.
def pozdrav(jmeno="světe"):
    return f"Ahoj, {jmeno}!"
    
print(pozdrav())  # Ahoj, světe!
print(pozdrav("Karel"))  # Ahoj, Karel!

# pokud chceme použít výchozí hodnotu, spolu s dalšími pozičními argumenty,
# musíme poziční argumenty deklarovat jako první. 
# Při volání pak musíme nejprve uvádět poziční argumenty,
# až poté nepoziční v libovolném pořadí.
def mocnina(cislo, exponent = 2):
	return cislo ** exponent
	
print(mocnina(2))   #2^2 = 4
print(mocnina(5, exponent = 3))   #5^3 == 125

# volání funkce pomocí jména parametrů
# při volání funkce můžeme použít jména agumentů, není pak potřeba dodržet pořadí
def info(jmeno, vek):
    return f"{jmeno} je starý {vek} let."

print(info(vek=25, jmeno="Karel"))  # Karel je starý 25 let.

Návratová hodnota

Funkce by obecně měli vracet nějakou hodnotu. Výsledek je uveden za klíčové slovo return, které se chová podobně jako break při práci s cyklem. Při vyhodnocení je vrácen výsledek a případná další část funkce se ignoruje. Z toho plyne, že funkce může obsahovat více klíčových slov return.

<aside> <img src="/icons/light-bulb_yellow.svg" alt="/icons/light-bulb_yellow.svg" width="40px" />

Obecně je lepší pokud funkce vrací jen hodnotu, její zpracování (výpis, další výpočet) je vhodné řešit mimo tělo funkce.

</aside>


# funkce, která přebýrá jeden parametr - známka jako číslo a vrací slovní hodnocení
def slovni_hodnoceni(znamka):
    if znamka == 1:
        return "výborné"
    elif znamka == 2:
        return "chvalitebné"
    elif znamka == 3:
        return "dobré"
    elif znamka == 4:
        return "dostatečný"
    elif znamka == 5:
        return "nedostatečný"
    else:
        print('špatně zadaná hodnota')
        
print(slovni_hodnoceni(3))

# pokud funkce nevrací žádnou hodnotu (neobsahuje return) bude její výsledek vždy None
def pozdrav():
    print("Ahoj!")

vysledek = pozdrav() #během přiřazování je vypsáno "Ahoj!" na konzoli
print(vysledek)  # None

Rekurzivní funkce

U některých složitějších algoritmů, jako je například procházení binárního stromu nebo algoritmy hledání nejkratší cesty v grafu nebo v matici, je zapotřebí využít rekurze. Rekurzivní funkce, jsou takové funkce, které jsou definovány pomocí sebe sama. Tedy uvnitř funkce je volána ta samá funkce. Základním příkladem rekurzivního algoritmu je funkce pro výpočet faktoriálu zadaného čísla:

def faktorial(n):
    if n == 1:  # Základní případ, výstupní podmínka, base case
        return 1
    else:
        return n * faktorial(n - 1)  

print(faktorial(5))  

U takto definovaných funkcí je nezbytně nutné správně definovat výstupní podmínku. Pokud by podmínka chyběla nebo byla špatně zapsaná, došlo by teoreticky k nekonečnému noření hlouběji a hlouběji. Je potřeba zmínit i fakt, že rekurze je obecně mnohem náročnější na výpočetní výkon a pokud je to možné, je vhodné se jí vyhnout.

Globální a lokální proměnná

Při práci s funkcemi je nutné pochopit jakým způsobem pracujeme s proměnnými uvnitř programu a funkcí. Pokud deklarujeme proměnnou uvnitř těla funkce, existuje pouze pro funkci (je lokální). Po volání je smazána.

def moje_funkce():
	x = 5
	return

moje_funkce()
print(x)  # NameError: name 'x' is not defined