prev up next title end end

4. Funkce

  1. Definice a použití
  2. Parametry a argumenty
    1. Poziční a párové entity
    2. Hvězdička a lomítko ve výčtu parametrů
    3. Parametry a argumenty *args
    4. Parametry a argumenty **kwargs
  3. Funkce čisté a nečisté I
  4. Funkce vyššího řádu
  5. Složené funkce
  6. Vnořené funkce, klauzury
  7. Glosář
  8. Cvičení

S pojmem funkce jsme se v předchozí kapitole již několikrát setkali, dokonce jsme některé již používali (str, int, type, print, ...). Jednalo se o takzvané vestavěné (built-in) funkce, které jsou součástí programového jádra Pythonu. Seznam vestavěných funkcí včetně jejich stručných popisů lze nalézt například zde.

S pojmem funkce úzce souvisí pojem metoda, což je funkce, definovaná uvnitř třídy (viz kap. 12) a je volaná pro objekt třídy (instanci) takzvanou tečkovou notací - jako například zde vestavěná metoda upper(), volaná (bez importu) pro objekt proměnné   hi :

>>> hi = "nazdar"                  # objekt typu <string>
>>> hi.upper()
'NAZDAR'

4.1 Definice a použití

Funkce je uspořádané pořadí příkazů, které provádějí požadovanou operaci pro zadaný výčet parametrů. Příkazy jsou deklarovány v těle funkce, parametry jsou uvedeny v záhlaví funkce. Jméno funkce, uvedené v záhlaví za příkazem def, je také názvem globální proměnné globálního prostoru souboru. Syntaxe pro definování funkce je tato:

def jméno_funkce(výčet parametrů):   # záhlaví ukončené dvojtečkou
    """
    docstring - nepovinný dokumentační řetězec
    """
    příkazy                          # odsazené tělo funkce

Jméno funkce může být libovolné, kromě klíčového slova Pythonu. Výčet parametrů představuje výčet očekávaných hodnot, které je nutné funkci při invokaci (volání) poskytnout. Tento výčet může být i prázdný.

Pod záhlavím lze umístit dokumentační řetězec (docstring), který obsahuje krátkou informaci o funkci.

Uvnitř těla funkce může být libovolný počet příkazů, ale všechny musí být odsazeny od levého okraje. Doporučovány jsou 4 mezery, nedoporučuje se použití tabulátoru. Přechod kurzoru na počátek nového řádku signalizuje konec definování funkce.

Definice funkce je jedním ze složených příkazů (if, while, for, with, try, funkce a třída), s kterými se postupně seznámíme. Všechy mají společný vzor:

  1. Záhlaví, které začíná klíčovým slovem a končí dvojtečkou.
  2. Tělo, skládající se z jednoho či více stejně či různě odsazených příkazů.

Zde je příklad deklarace či definice funkce bez parametrů (a bez docstringu):

def novy_riadok():        # uživatelská funkce bez parametru
    print()               # invokace vestavěné funkce 'print'

Funkce se jmenuje novy_riadok. Prázdné závorky udávají, že je bez parametru. Její tělo obsahuje jediný příkaz, jímž je volání vestavěné funkce print(), která bez argumentu vrátí prázdný řádek. Deklaraci funkce si uložíme do souboru s názvem emptyLines.py.

Definování nové funkce ještě nespustí její provádění. K tomu musíme funkci volat (invokovat). Při volání (invokaci) funkce zadáváme její jméno a výčet hodnot, kterým v tomto případě říkáme argumenty. Ty jsou přiřazeny k definovaným parametrům funkce. Závorky jsou vždy povinné:

emptyLines.py

def novy_riadok(): 
    """ vrátí prázdný řádek """            
    print()             
            
print("First Line")     # volání fce 'print' s argumentem
novy_riadok()                       # volání nové funkce
print("Second Line")    # volání fce 'print' s argumentem
Výstupem tohoto programu v konzole IDLE nebo Thonny je:
First Line
              # mezera je vytvořena volanou funkcí print()
Second Line

Poznámka: Soubor emptyLines.py lze rovněž spustit ze systémové konzoly, pokud se "nacédujeme" do složky, v níž máme soubor uložený. V systémové konzole potom zadáme (například):

F:\Codetest\HowTo\ch-03> python emptyLines.py
First Line

Second Line

Nejjednodušší je funkce, která nic neprovádí:

def cosi():
    pass                      # 'pass' je klíčové slovo

4.2 Parametry a argumenty

Hodnotám, které zadáváme při volání funkce, říkáme argumenty. Tyto hodnoty jsou přiřazeny k parametrům v deklarovaném záhlaví funkce. Parametry jsou (zjednodušeně řečeno) výčtem jmen, argumenty jsou výčtem hodnot. Jednotlivé parametry výčtu představují formálně lokální proměnné funkce.

Parametr s přiřazenou hodnotou (např. b="bim"), uvedený v záhlaví funkce, se označuje jako implicitní (default) argument.
Jméno s přiřazenou hodnotou (např. b="bim"), uplatněné při volání funkce, se označuje jako pojmenovaný (keyword) argument.
V dalším textu použijeme označení párový parametr pro implicitní argument a párový argument pro pojmenovaný argument.

Jednotlivé kategorie parametrů a argumentů jsou (podle pořadí zleva doprava) tyto:
poziční, variadické poziční, párové, variadické párové.

Poziční parametry jsou ty, o jejichž hodnotě se rozhoduje na základě pořadí přiřazovaných pozičních argumentů.

Variadické parametry *args a **kwargs jsou hvězdičkami dekorovaná jména kontejnerů předem neurčené délky pro poziční a párové argumenty.

4.2.1  Poziční a párové entity

Mezi pozičními a párovými parametry či argumenty existuje zdánlivě benevolentní vztah, který však respektuje požadavek aby se poziční argument neocitl za párovým.

def mocnina(m, n=2):   # m je poziční, n=2 je párový parametr,
    print(m**n)        # pořadí je OK        
>>> mocnina(3)          #1 OK: n=2 - párový parametr
9
>>> mocnina(3,2)        #2 OK: n=2 - potvrzené přiřazení
9
>>> mocnina(3,n=2)      #3 OK: n=2 - potvrzené přiřazení
9
>>> mocnina(3,n)        #4 asi bezpečnostní pojistka:
NameError: name 'n' is not defined    
>>> mocnina(2,3)        #5 OK: n=3 - změna přiřazení     
8
>>> mocnina(m=2,n=4)    #6 OK: oba argumenty jsou párové
16
>>> mocnina(m=2,3)      #7 porušení pravidla:      
SyntaxError: positional argument follows keyword argument

Poznámka ad #4: Zde nás Python škádlí. Chce abychom při volání funkce využili již přiřazenou hodnotu (tudíž ji neuváděli) nebo ji opětovně deklarovali (n=2) nebo abychom ji přiřadili hodnotu jinou (např. n=2.0).
Poznámka ad #6: Jak vidno, do místa deklarovaného pozičního parametru (m) lze dosadit i párový argument, pokud není následován pozičním argumentem.

4.2.2  Hvězdička a lomítko ve výčtu parametrů

Hvězdička * ve výčtu parametrů přikazuje, aby všechny parametry, které za ní následují, přijímaly pouze párové argumenty:

def foo(a, b, *, c, d):          # hvězdička jen organizuje      
    print(a, b, c, d)
>>> foo(2, 4, c="jin", "jan")    # 2,4,"jan" jsou poziční argumenty
SyntaxError: positional argument follows keyword argument
>>> foo(2, 4, c="jin", d="jan")  # c,d jsou párové argumenty     
2 4 jin jan                      # no comment

Lomítko / ve výčtu parametrů přikazuje, aby všechny předcházející parametry přijímaly pouze poziční argumenty:

def f(a, b, /, c, d, *, e, f):    # lomítko jen organizuje 
    print(a, b, c, d, e, f)

Mezilehlé argumenty pro parametry c, d mohou být poziční i párové.

>>> f(10, 20, 30, d=40, e=50, f=60)
10 20 30 40 50 60
>>> f(10, a=20, 30, d=40, e=50, f=60)
SyntaxError: positional argument follows keyword argument
>>> f(10, b=20, c=30, d=40, e=50, f=60)
TypeError: f() got some positional-only arguments passed as keyword arguments

Některé vestavěné funkce, například len(obj), připouštějí jenom poziční argumenty.

>>> len(obj='hello')
TypeError: len() takes no keyword arguments

4.2.3  Parametry a argumenty *args

Variadické argumenty *args mají formát entice (tuple), jejímiž prvky jsou poziční argumenty, případně párové argumenty key := value, nebo slovník s páry 'key' : 'value'.

Invokace funkce s variadickým parametrem typu *args a s vnořenou vestavěnou funkcí print():

>>> def myFun(*argv):
        for arg in argv:
            print(arg)   # tato funkce poskytuje více možností
# Argumenty funkce:		
>>> myt = (a:="Tam za vodou", b:="v rákosí ...")   # entice
>>> myd = {a:"Tam za vodou", b:"v rákosí ..."}     # slovník
# Různé invokace funkce:
>>> myFun(*myt); myFun(*myd)
>>> myFun(a:="Tam za vodou", b:="v rákosí ...")
>>> myFun(*{a:"Tam za vodou", b:"v rákosí ..."})
# Shodný výstup čtyř možných invokací:
Tam za vodou
v rákosí ...
# ... porovnejte s invokací (jen tak, pro zajímavost):
>>> myFun(myt); myFun(myd)

# Pozor na způsob přiřazení:
>>> myt = (a: "Tam za vodou", b: "v rákosí ...")
             ^                  ^    # chybí rovnítka
SyntaxError: invalid syntax          # myd je OK		

Další příklad: Vestavěná funkce max(...) vrátí největší ze zadaných argumentů. V následné ukázce je variadická entice použita jako argument (variadický parametr je zřejmě obsažen v definici funkce).

Při invokaci vestavěné funkce lze pro variadický parametr uplatnit buď výčet hodnot nebo variadický či prostý argument:

>>> num1 = 7,11
>>> max(7,11), max(num1), max(*num1)
(11, 11, 11)

>>> num2 = 3*11,5**3,512-9,1024*0 
>>> max((3*11,5**3,512-9,1024*0), max(num2), max(*num2))
(503, 503, 503)

>>> str = "abakus"   
>>> max("abakus"), max(str), max(*str)
('u', 'u')               # pořadí je dáno přiřazenými kódovými čísly
def add(*number):        # entice jako variadický parametr
    result=0
    for i in number:
         result +=i
    return result
>>> add(1,2,3,4,5)        # přes poziční argumenty
15
>>> num = (47,11,12)  
>>> add(*num)             # přes variadickou proměnnou
70
add(num)                  # přes prostou proměnnou
TypeError: unsupported operand ...

Kombinace parametrů s *args

Pro ilustraci variadického parametru (*b) vytvoříme funkci, ve které použijeme i poziční i párový parametr:

def sum_not_first(a,*b,c=8):   # poziční, variadický, párový parametr 
    temp = sum(b)
    print(f"sum(b)=", temp, " a,b,c =",a,b,c)
    print(f"sum_not_first=sum(b)+c=", temp+c)

Variadický poziční parametr *b posbírá všechny volné poziční argumenty a vloží (zabalí) je do entice:

>>> sum_not_first(1, 2, 3, 4)       # c=8 stále platí!
sum(b)= 9  a,b,c = 1 (2, 3, 4) 8    # o '1' si řekne par. 'a'
sum_not_first=sum(b)+c= 17          

Jinak tomu je s variadickým parametrem na prvním místě. Za variadickým pozičním argumentem smějí přijít jen párové argumenty!:

def sum_not_first(*b,c=8,a):      # zatím nic proti ničemu
    temp = sum(b)
    print(f"sum(b)=", temp, " a,b,c =",a,b,c)
    print(f"sum_not_first=sum(b)+c=", temp+c)
>>> sum_not_first(1, 2, 3, 4)     # chyba se projeví až při invokaci        
TypeError: sum_not_first() missing 1 required keyword-only
           argument: 'a'          # chybí 1 párový argument

Argument pro 'a' měl být zadán jako párový, protože za variadickým i párovým (c=8) argumentem nesmí přijít poziční argument, byť je jako poziční parametr uveden (a):

>>> sum_not_first(1, 2, 3, a=4)            # a=4: náprava chyby          
sum(b)= 6  a,b,c = 4 (1, 2, 3) 8
sum_not_first=sum(b)+c= 14

Nyní si ukažme pozici *b na konci výčtu:

def sum_not_first(a,c=8,*b):           
    temp = sum(b)
    print(f"sum(b)=", temp, " a,b,c =",a,b,c)
    print(f"sum_not_first =", temp+c)
>>> sum_not_first(1, 2, 3, 4)               # něma problema   
sum(b)= 7  a,b,c = 1 (3, 4) 2
sum_not_first = 9          

Podle klobacího pořádku obdržel parametr 'c' hodnotu 2. Jak vidno, párový parametr je pěkný nezdvořák. Sáhne si po nabídnuté hodnotě i když již přiřazenou hodnotu má.

Lze rovněž použít pouze prvky sběrného parametru:

def sum_skip_first(a,*b):
    temp = sum(b)               # sum() je vestavěná funkce               
    print(f"a, b, sum(b) is: ", a, b, temp)
>>> sum_skip_first(1, 2, 3, 4)    
a, b, sum(b) is:  1 (2, 3, 4) 9          

Variadický formát lze použít i při obsazování pozičních parametrů:

def test_args(a, b, c):
    print("Virbl:", (b + c) * a)  # hvězdička zde značí součin
>>> slova=(" abra", "kadabra")    # argumentem je entice
>>> test_args(2, *slova)          # hvězdička signalizuje entici
Virbl: abrakadabra abrakadabra

Případně:

>>> pairs={" hola": "b", "hej": "c"}   # argumentem je slovník
>>> test_args(2, *pairs)             # hvězdička signalizuje entici
Virbl: holahej holahej
# Enticové rozbalení slovníku vrací pouze klíče slovníku.

4.2.4  Parametry a argumenty **qwargs

Variadické parametry a argumenty **kwargs mají formát slovníku, jehož prvky jsou slovníkové páry 'key':'value' , případně páry jméno=hodnota.

# kwargs.py
def about(**users):
    for key,val in users.items():
        print(f"{key} => {val}")

a) Na elementy slovníku odkážeme při invokaci funkce:

>>> log={'user':'Tom', 'city':'London', 'pet':['dog','cat','fish']}
>>> about(**log)              # argument má formát slovníku
user => Tom
city => London
pet => ['dog', 'cat', 'fish']

b) Výpis párových argumentů (jméno=hodnota) vypíšeme při invokaci funkce:

# zde je argumentem výčet párů (bez rozbalení):
>>> about(user='Tom', city='London', pet=['dog','cat','fish'])
user => Tom
city => London
pet => ['dog', 'cat', 'fish']

U argumentů typu **kwargs použijeme odkaz na skutečný slovník nebo výpis párových argumentů**.
**Apropos ad b): Stejný formát (výpis párových argumentů) používá vestavěná metoda dict() - viz kap. 11.1.

Kombinace parametrů s **qwargs

Sběrné parametry mohou posloužit jako šikovný 'vysavač' pro nadbytečně zadané argumenty:

def eta(a, b, *c, **d):     # a,b jsou poziční parametry
    print((a + b), c)
>>> eta(2, 3, 4, 6)     # parametr *c vyluxoval co zbylo,
5 (4, 6)                # na parametr **d nezbylo nic

Půvabná je rovněž tato ukázka rozbalení sběrných parametrů u slovníku:

def moje_troje(a, b, c):
    print(a, b, c)
>>> a = {'i': "one", 'j': "two", 'k': "three" }  # slovník
>>> moje_troje(*a)   
i j k          # "Enticové" rozbalení slovníku vrací  klíče slovníku.
>>> moje_troje(**a)                
one two three  # "Slovníkové" rozbalení slovníku vrací hodnoty slovníku.

Při luxování argumentů musí mít interpret jasno, co kam může přijít. Základní instrukce obdrží z výčtu parametrů v záhlaví funkce. Při invokaci funkce pro aktuální výčet argumentů provede příkazy z těla funkce:

def foo(a, b, *args, c, d, **kwargs):        
    print(a, b, args, c, d, kwargs)   
td = {"m":109,"n":110}         
kwd = {"q":113,"r":114}        

Proměnné td, kwd mají formát slovníku. Lze je uplatnit jako prosté argumenty td, kwd, variadické argumenty *td, *kwd (entice) a jako slovník **kwd:

foo(1,2,3,4,td,'oko',c=6,d='nos',kwd)
SyntaxError: positional argument follows keyword argument

Viníkem je kwd, zadali jsme jej jako prosté poziční jméno. Zkusme jiné invokace:

#1 prosté uplatnění 'td' a enticové rozbalení '*kwd':
>>> foo(1,2,3,4,td,'oko',c=6,d='nos',*kwd)
1 2 (3, 4, {'m': 109, 'n': 110}, 'oko', 'q', 'r') 6 nos {}
  
#2 'enticové' rozbalení '*td' i '*kwd':
>>> foo(1,2,3,4,*td,'oko',c=6,d='nos',*kwd)
1 2 (3, 4, 'm', 'n', 'oko', 'q', 'r') 6 nos {}
           
#3 prosté uplatnění 'td' a slovníkové rozbalení '**kwd':
>>> foo(1,2,3,4,td,'oko',c=6,d='nos',**kwd)
1 2 (3, 4, {'m': 109, 'n': 110}, 'oko') 6 nos {'q': 113, 'r': 114}

#4 enticové rozbalení 'td' a slovníkové rozbalení '**kwd':
>>> foo(1,2,3,4,*td,'oko',c=6,d='nos',**kwd)
1 2 (3, 4, 'm', 'n', 'oko') 6 nos {'q': 113, 'r': 114}  

Ve shodě s kap. 11.3 umíme rozbalit i hodnoty slovníků:

>>> foo(1,2,3,4,*td.values(),'oko',c=6,d='nos',*kwd.values())
1 2 (3, 4, 109, 110, 'oko', 113, 114) 6 nos {}

Nepřehlédněme, že se nám rozbalené hodnoty (i klíče) slovníků přiřazují do variadické entice.


4.3 Funkce čisté a nečisté I

Vestavěné funkce, které jsme zatím používali, jako abs, pow a max nám vracely konkrétní číselné výsledky. Volání každé z těchto funkcí generuje hodnotu, kterou obvykle přiřadíme k proměnné nebo použijeme jako součást výrazu:

>>> biggest = max(3, 7, 2, 5); biggest          # --> 7
>>> x = abs(3 - 11) + 10; x                     # --> 18

Ve vlastních funkcích jsme vesměs používali vestavěnou funkci print, která nám vracela různá sdělení, jímž však mohl být i výsledek výpočtu, například:

def area(rad):       # 'rad' je zde poloměr kruhové plochy
    print(3.14*rad**2)           # area(10) -->> 314.0 

To, že funkce print zajistí výtisk výsledku číselné operace, ještě neznamená, že funkce area vrací dále použitelný výsledek. Pokusíme-li se tento výsledek použít při volání jiné funkce, například:

def suma(rad, b):
   print(area(rad) + b)       
>>> suma(10, 100)
314.0                            # výstup z fce area(rad)
TypeError: unsupported operand types: 'NoneType' and 'int' 

dostáváme chybové hlášení, že se pokoušíme sečíst nesourodé operandy. Nahradíme-li však v první funkci volání print příkazem return, budeme moci její výstup v další funkci použít:

def area(rad):  
   return  3.14*rad**2        # area(10) -->> 314.0

def suma(rad, b):
   print(area(rad) + b)       # suma(10,100) -->> 414.0

Příkaz return nejenom vrací dále použitelnou hodnotu (pokud taková existuje) ale také ukončí provádění funkce v místě svého výskytu.

Úspěch úspěšné varianty funkce suma je důsledkem toho, že jsme jako argument funkce print použili takzvanou čistou variantu funkce area.

Čistá funkce nemění hodnotu vstupu, nemá vedlejší účinky a pro stejný vstup vrací vždy stejný výsledek. Vedlejší účinky produkují funkce print a input.
Pokusíme-li se tedy použít výstup funkce suma jako hodnotu pro další výpočet, dostaneme opět chybové hlášení, neboť složená funkce suma obsahuje funkci print.

Funkce, která mění vstupní hodnoty je označována jako funkce nečistá (inpure) neboli modifikátor.

Použití příkazu return samo o sobě čitotu funkce nezaručuje. Ukážeme si to na příkladu dvou funkcí, které přijmou seznam jako argument a vynásobí dvěma každou jeho položku.

Zde je double_stuff_p jako čistá funkce, která nemění zadaný seznam:

def double_stuff_p(a_list):          
    new_list = []                # zárodek nového seznamu 
    for value in a_list:
        new_list += [2*value]      
    return new_list    
>>> lst = ["hi", 22, True]     # True bude při kompilaci redukováno na 1
>>> double_stuff_p (lst)
['hihi', 44, 2]
>>> lst
['hi', 22, True]               # vstupní seznam nezměněn

Opakovaným voláním funkce čisté pro stejné argumenty dostáváme vždy stejný výstup, a to bez vedlejších účinků, například modifikace vstupních údajů.
Poznámka: Smyčka for ... (Kap. 5.3) a funkce range() (Kap. 5.6) vytvářejí iterátor (Kap. 3.2).

Příklad téže funkce jako nečisté. Tato funkce mění každým voláním prvky zadaného argumentu i hodnotu výstupu přesto, že končí příkazem return. Je to způsobeno skladbou samotné funkce:

def double_stuff_m(a_list):         # argumentem bude seznam
    for i in range(len(a_list)):    # postupná změna vstupu
        a_list[i] = a_list[i] * 2
    return a_list		
>>> lst = ["hi", 22, True]
>>> double_stuff_m(lst); lst
['hihi', 44, 2]
['hihi', 44, 2]                 # hodnota lst se mění!
>>> double_stuff_m(lst)             
['hihihihi', 88, 4]             # mění se i výstup při opakovaném volání 

Co je lepší?

Má se za to, že čisté funkce se snadněji tvoří a jsou méně náchylné k chybám než programy, které používají modifikátory. Nicméně, programy s čistými funkcemi jsou někdy méně výkonné.


4.4 Funkce vyššího řádu

Funkce či metoda, která přijme jinou funkci jako argument nebo která jinou funkci vrací, se nazývá funkce (metoda) vyššího řádu.

Příkladem takové funkce budiž níže uvedená funkce doto():

def f(n):
   return 3*n - 6
def g(n):
   return 5*n + 2
def h(n):
   return -2*n + 17
def doto(value, fun):  # argumentem má být hodnota a funkce       
   return fun(value)
>>> doto(7, f)
15           
>>> doto(7, g)
37           
>>> doto(7, h)
3          

Funkce doto je volána třikrát pro stejný číselný argument a pro tři různá jména čistých funkcí.

Jiný příklad funkce vyššího řádu:

def print_haf():               # prostá složená funkce 
    print('Haff')

def do_twice(miau):            # funkce vyššího řádu       
    miau(); miau()             # viz Poznámka 	

Poznámka: Z textu v těle funkce vyplývá, že parametr miau očekává název funkce při invokaci:

>>> do_twice(print_haf)      
Haff
Haff

Při volání funkce do_twice je parametr 'miau' nahrazen argumentem, jímž je název funkce print_haf.

Funkcemi vyššího řádu jsou i dekorátory - viz Kap. 12.7.
V kapitole 5.8 jsou popsány vestavěné funkce vyššího řádu   map(), reduce(), filter(), zip().


4.5 Složené funkce

Kromě toho, že můžeme funkci zadat jiné funkci jako argument (jak popsáno v předchozím odstavci u funkce vyššího řádu), můžeme uvnitř funkce volat jinou, případně tutéž funkci. V obou případech hovoříme o složené funkci, ve druhém navíc o rekurzivní (složené) funkci.
Rekurzivní funkci poznáme v Kap. 13.2.

Jako další příklad prosté složené funkce (nikoliv funkce vyššího řádu) si napíšeme funkci, která pro zadané dva body (střed kružnice a bod na obvodu) vypočítá plochu kruhu.

Střed kružnice uložíme do proměnných xc,yc, bod obvodu do xp,yp.
Prvním krokem bude určení poloměru kružnice, což je vzdálenost mezi oběma body.
Dalším krokem bude výpočet plochy.

Pro určení vzdálenosti dvou bodů si napíšeme funkci distance, pro určení plochy si napíšeme funkci area, přičemž použijeme importovanou funkci sqrt (square root) a konstantu pi z modulu math :

import math    # potřebujeme funkci 'sqrt' a konstantu 'pi'
def distance(xc,yc, xp,yp):
    return math.sqrt((xp-xc)**2 + (yp-yc)**2)
def area(rad):
    print(math.pi*rad**2)

Pro další výpočet si vytvoříme dočasné proměnné,:

radius = distance(xc,yc, xp,yp)
result = area(radius)  

které vložíme do další funkce:

def area2(xc, yc, xp, yp):
    radius = distance(xc, yc, xp, yp)
    result = area(radius)
    return result

Funkci jsme pojmenovali area2, abychom ji odlišili od funkce area, definované dříve. V jednom skriptu můžeme mít jenom jednu funkci daného jména.

Dočasné proměnné radius a result jsou užitečné jenom pro rozvoj programu a vychytání chyb. Jakmile nám program pracuje správně, můžeme jej zestručnit spojením funkcí.

def area2(xc, yc, xp, yp):
    return area(distance(xc, yc, xp, yp))

Výsledkem je funkce s odkazy na funkce, deklarované mimo tělo aktuální funkce. Při volání této funkce musí mít interpret k disposici deklarace zmiňovaných fukcí.

>>> area2(20, 20, 0, 10)
1570.7963267948967

Funkce, která přijímá jinou funkci jako argument, se řadí mezi funkce vyššího řádu. S tímto typem funkcí jsme se setkali již v odstavci 4.4.


4.6 Vnořené funkce, klauzury

Jak již víme, vnořené (vnitřní) funkce jsou funkce, deklarované uvnitř jiných funkcí. Vnitřní funkce má přístup k proměnným a argumentům vnější funkce i k proměnným globálního prostoru, pokud nejsou zastíněny, to jest, pokud neexistují stejnojmenné proměnné v prostoru vnitřní funkce. Vnější funkce ale nemá přístup k proměnným a argumentům vnitřní funkce.

Název vnější funkce, zapsané v globálním prostoru souboru, tvoří globální proměnnou tohoto prostoru.

y = 8                          # globální proměnná y

def outer_var():               # vnější funkce
    x = 2                      # lokální proměnná vnější fce 
    def inner_var():           # vnitřní funkce
        x = 6                  # lokální proměnná vnitřní fce
        print ("inner =", x + y)     # příkaz vnitřní funkce
    print ("outer =", x + y)   # příkaz vnější funkce 
    inner_var()                # invokace vnitřní funkce

Proměnná vnitřní fce (x=6) stíní proměnnou vnější fce (x=8). Obě funkce mají přístup ke globální proměnné y=8:

>>> outer_var()                # invokace vnější funkce
outer = 10                     # x,y = 2,8
inner = 14                     # x,y = 6,8

Pokud bychom potřebovali aby hodnota proměnné vnitřní funkce byla přístupná i z prostoru vnější funkce, deklarujeme ji jako nonlocal:

a = "piroh"                           # globální proměnná
def outer_fce():                      # deklarace vnější fce
    a = 5                             # lokální proměnná vnější fce
    def inner_fce():                  # deklarace vnitřní fce
        nonlocal a                    # deklarace 'nelokálnosti'
        a = 10                        # lokální proměnná vnitřní fce
        print("Vnitřní fce: a = ", a) # příkaz vnitřní fce
    print("Vnější fce1: a = ", a)     # příkaz vnější fce
    inner_fce()                       # volání vnitřní fce 
    print("Vnější fce2: a = ", a)     # příkaz vnější fce
outer_fce()                           # volání vnější fce
Vnější fce1: a =  5
Vnitřní fce: a =  10
Vnější fce2: a =  10                  # původně bylo a = 5

Kdybychom vypustili deklaraci nelokálnosti (# nonlocal a), dostali bychom tento výstup:

Vnější fce1: a =  5
Vnitřní fce: a =  10
Vnější fce2: a =  5

Kdybychom zrušili všechny proměnné 'a' uvnitř funkcí, dostali bychom tento výstup:

Vnější fce1: a =  piroh
Vnitřní fce: a =  piroh
Vnější fce2: a =  piroh

Kdybychom místo deklarace nonlocal použili ve vnitřní funkci deklaraci global, dostali bychom tento výstup:

Vnější fce1: a =  5
Vnitřní fce: a =  10
Vnější fce2: a =  5

Kdybychom deklaraci global použili ve vnější funkci, dostali bychom tento výstup:

Vnější fce1: a =  5
Vnitřní fce: a =  10
Vnější fce2: a =  5

Klíčová slova nonlocal a global lze použít v těchto jmenných prostorech:

Velkým korektorem přístupnosti je chyba typu UnboundLocalError: local variable 's' referenced before assignment, jak lze demonstrovat na následující ukázce:

def ule():
   global s
   print("one: s = ", s)          # první reference 's'
   s = "zlatíčko"
   print("two: s = ", s)          # druhá reference 's'
>>> s = "Hordubal"; ule()
one: s =  Hordubal
two: s =  zlatíčko
>>> print("three: s = ", s)       # třetí reference 's'
three: s =  zlatíčko          

Bez deklarace "global s" bychom obdrželi výše uvedený UnboundLocalError. Deklarace učiní globální proměnnou s="Hordubal" přístupnou první referenci 's' uvnitř funkce. Druhá reference 's' má již k disposici proměnnou s="zlatíčko", pro kterou deklarace globálu neplatí, protože je stíněná 'zlatíčkem'. Třetí reference 's' mimo funkci musí použít deklarovanou globální hodnotu "zlatíčko", která zakrývá (stíní) skutečnou globální hodnotu "Hordubal".

Klauzury (closures)

Pokud vnořená funkce odkazuje na proměnnou, deklarovanou v rámci vnější funkce a pokud vnější funkce vrací vnitřní funkci, označujeme vnořenou funkci jako klauzuru. Vnitřní funkce může použít parametr (x), deklarovaný vnější funkcí:

def num_outer(x):                # vnější funkce
   def num_inner(y):             # vnitřní funkce
      return x * y               # výstup z vnitřní funkce
   return num_inner              # výstup z vnější funkce

Volání naší vnější funkce vrací vnitřní funkci pro argument vnější funkce:

>>> num_outer(10)              # chybí hodnota parametru y       
<function num_outer.<locals>.num_inner at 0x03DE2028>

Volání klauzury lze provést jedinou invokací:

>>> num_outer(12)(4)
48

Nebo ji lze volat ve dvou krocích s použitím pomocné proměnné:

>>> huk = num_outer(12)          # x = 12
>>> huk(4)                       # y =  4
48

Vnitřní funkce je zde vázaná na hodnotu x=12, pročež lze funkci num_outer případně smazat a zadávat argument jen pro vnitřní funkci:

>>> del(num_outer)
huk(5)                           # x = 12, y = 5
60

Vnější funkce s proměnnou:

def pat():               # vnější funkce
   msg = "Jsem 'pat'"             # proměnná vnější funkce                                        
   def mat():            # vnitřní funkce
      print(msg)                  # příkaz vnitřní funkce
   return mat                     # příkaz vnější funkce
>>> pat()()
 Jsem 'pat'
>>> bat = pat(); bat()
 Jsem 'pat'

4.7 Glosář

funkce
Pojmenovaný sled příkazů, který provede nějakou užitečnou operaci. Funkce mohou nebo nemusí mít parametry a mohou nebo nemusí produkovat výsledek.
čistá funkce (pure function)
Funkce bez vedlejších účinků. Čistá funkce nemění hodnotu vstupních argumentů.
funkce modifikační
Funkce, která mění hodnotu zadaných argumentů.
složený příkaz (compound statement)
Příkaz, který se skládá ze dvou částí:
  1. záhlaví - začíná klíčovým slovem pro příkaz a končí dvojtečkou
  2. tělo - obsahuje jeden nebo více příkazů, které jsou stejně odsazené zleva
volání (invokace) funkce
Příkaz k provedení funkce. Skladá se z jména funkce před závorkou se seznamem argumentů.
průběh výpočtu (flow of execution)
Pořadí, ve kterém jsou příkazy prováděny během běhu programu.
booleovská funkce (boolean function)
Funkce, která vrací booleovskou hodnotu.
složení funkcí (composition of functions)
Volání jedné funkce z těla druhé, nebo použití výstupní hodnoty jedné funkce jako argument při volání druhé.
parametr
Jméno v záhlaví funkce, které se vztahuje k hodnotě, poskytnuté ji (při volání) jako argument.
volitelný parametr (optional parameter)
Parametr uvedený v záhlaví funkce s přiřazenou počáteční hodnotou, která se použije, když při volání funkce není pro tento parametr zadán argument.
argument
Hodnota, poskytovaná volané funkci. Tato hodnota je přiřazena odpovídajícímu parametru funkce.
import
Příkaz, kterým do skriptu nebo interpretační konzoly vložíme (importujeme) soubor, funkci nebo proměnnou, definovanou v jiném skriptu.
výstupní hodnota (return value)
Hodnota, která je výsledkem volání funkce.
spojení funkcí (function composition)
Výstup z jedné funkce použijeme jako vstup pro druhou.
lokální proměnná (local variable)
Proměnná, která je definovaná uvnitř funkce, kde pouze smí být užita.

4.8 Cvičení

  1. Zabalte následující kód do funkce:  compare(x,y) a vložte do souboru compare1.py:
    if x < y :
        print(x, "je menší než", y)
    elif x > y :
        print(x, "je větší než", y)
    else: 
        print(x, "a", y "jsou stejné")
    
    V prostředí IDLE ji volejte třikrát - s prvním argumentem menším, větším a stejným (než) jako druhý argument.
  2. Program pro výpočet výnosu z vkladu, vytvořený v 5. cvičení předchozí kapitoly, zapište jako funkci. Volejte ji pro různé argumenty.
    def vynos(p, r, *, t=10, n=12):
    
  3. S použitím textového editoru v IDLE vytvořte skript lenRiadky.py, do něhož napište funkci jménem nine_lines, která použije importovanou funkci tri_riadky ze souboru emptyLines.py k vytištění devíti prázdných řádek. Poslední řádkou skriptu by mělo být volání fce nine_lines.
  4. Doplňte tělo definované funkce cat_times tak, aby n krát vytiskla řetězec s:
    def cat_times(s,n):
        <zde zapište svůj kód>
    Vložte tento skript do souboru caclr-ttimes.py ve složce kap-03. Tam se nacédujte v cmd.exe a na příkazový řádek zapište python. V prostředí interpreta Pythonu zkuste následující:
    >>> from caclr-ttimes import *
    >>> cat_times('Spam', 7)
    SpamSpamSpamSpamSpamSpamSpam
    
    Chodí? Vyzkoušejte si funkci i pro jiné argumenty.

pre up next title end end