S pojmem funkce jsme se v předchozí kapitole již několikrát setkali, dokonce jsme některé již používali (
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
>>> hi = "nazdar"# objekt typu <string> >>> hi.upper() 'NAZDAR'
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 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:
Zde je příklad deklarace či definice funkce bez parametrů (a bez docstringu):
def novy_riadok ():# uživatelská funkce bez parametru # invokace vestavěné funkce 'print'
Funkce se jmenuje
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
Výstupem tohoto programu v konzole IDLE nebo Thonny je:emptyLines.py def novy_riadok ():""" vrátí prázdný řádek """ # volání fce 'print' s argumentem novy_riadok()# volání nové funkce # volání fce 'print' s argumentem
First Line# mezera je vytvořena volanou funkcí print() Second Line
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
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ě
Parametr s přiřazenou hodnotou (např. b="bim"), uvedený v záhlaví funkce, se označuje jako
Jméno s přiřazenou hodnotou (např. b="bim"), uplatněné při volání funkce, se označuje jako
V dalším textu použijeme označení párový parametr pro
Jednotlivé kategorie parametrů a argumentů jsou (podle pořadí zleva doprava) tyto:
poziční, variadické poziční, párové, variadické párové.
Poziční
Variadické
Při zadávání argumentů deklarované funkci jsou kontrolovány tyto aspekty:
>>> li = [1,2,3,4] >>> def mysum(arg):# funkce pro 1 argument print(sum(arg)) >>> mysum(li)# 1 argument 10 >>> mysum(1,2,3,4)# 4 argumenty TypeError ...
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, # 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 ... poziční argument nesmí přijít za párovým
Hvězdička
def foo (a, b, *, c, d):# hvězdička jen organizuje
>>> 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
def f (a, b, /, c, d, *, e, f):# lomítko jen organizuje
>>> 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 # dle definice mají být první dva argumenty poziční
Některé vestavěné funkce, například
>>> len(obj='hello')TypeError: len() takes no keyword arguments
Variadické
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
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=0for iin number: result +=ireturn 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 ...
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)
Variadický poziční parametr
>>> 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ě.
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)
>>> 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
>>> 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):# 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.
Variadické
# kwargs.py def about (**users):for key,valin users.items():
a) Invokace funkce pro
>>> 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) Invokace funkce pro
# 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']
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
>>> 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):
>>> 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):
Proměnné
foo(1,2,3,4,td ,'oko',c=6,d='nos',kwd )SyntaxError: positional argument follows keyword argument
Viníkem je
#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.
Vestavěné funkce, které jsme zatím používali, jako
>>> 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
def area (rad):# 'rad' je zde poloměr kruhové plochy # area(10) -->> 314.0
To, že funkce
def suma (rad, b):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í
def area (rad):return 3.14*rad**2# area(10) -->> 314.0 def suma (rad, b):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
Čistá funkce nemění hodnotu vstupu, nemá vedlejší účinky a pro stejný vstup vrací vždy stejný výsledek. Vedlejší účinky produkují funkce
Pokusíme-li se tedy použít výstup funkce
Funkce, která mění vstupní hodnoty je označována jako funkce nečistá (inpure) neboli modifikátor.
Použití příkazu
Zde je
def double_stuff_p (a_list): new_list = []# zárodek nového seznamu for valuein 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ů.
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
def double_stuff_m (a_list):# argumentem bude seznam for iin range (len (a_list)):# postupná změna vstupu a_list[i] = a_list[i] * 2return a_list
>>> lst = ["hi", 22, True] >>> double_stuff_m(lst) ['hihi', 44, 2]# hodnota lst se mění! >>> double_stuff_m(lst) ['hihihihi', 88, 4]# mění se i při opakovaném volání
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é.
Funkce či metoda, která přijme jinou funkci jako argument nebo která jinou funkci vrací, se nazývá
Příkladem takové funkce budiž níže uvedená funkce
def f (n):return 3*n - 6def g (n):return 5*n + 2def h (n):return -2*n + 17def 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
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
>>> do_twice(print_haf) Haff Haff
Při volání funkce
Funkcemi vyššího řádu jsou i dekorátory - viz Kap. 10.7.
V kapitole 5.9,10 jsou popsány vestavěné funkce vyššího řádu
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. 11.1.
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
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
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):
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
Dočasné proměnné
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.
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
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 # příkaz vnitřní funkce # 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
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 # příkaz vnitřní fce # příkaz vnější fce inner_fce()# volání vnitřní fce # 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
Vnější fce1: a = 5 Vnitřní fce: a = 10 Vnější fce2: a = 5
Kdybychom deklaraci
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
def ule ():global s# první reference 's' s = "zlatíčko"# 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
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
>>> 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 # příkaz vnitřní funkce return mat# příkaz vnější funkce
>>> pat()() Jsem 'pat' >>> bat = pat(); bat() Jsem 'pat'
Speciálním druhem funkcí jsou takzvané anonymní funkce, které používají společné označení lambda. Na rozdíl od ostatních funkcí se deklarují přímo v místě použití. Jejich syntaxe je velice prostá:
lambda parametr: výraz argument
Funkce lambda může mít libovolný počet parametrů ale jen jeden deklarovaný výraz (expression). Pro opakované použití je možné opatřit tuto funkci jménem:
>>> (lambda x, y: x * y)(2, 3)# bezprostředně volané provedení funkce 6 >>> soucin = lambda x, y: x * y# jméno umožňuje opakované použití >>> soucin(2, 3) 6
Anonymní funkci lze s výhodou použít jako argument pro jinou funkci (připadně ještě s jinou funkcí):
>>> numera = (2, 6, 8, 10, 11, 4, 12, 7, 13, 17, 0, 3, 21) >>> selekce = list(filter(lambda num: (num > 7), numera))lambda parametr: výraz argument >>> print(selekce) [8, 10, 11, 12, 13, 17, 21]
Ve funkci lambda lze rovněž použít variadický parametr
# parametry funkce jsou hodnoty (values) slovníku 'slov': >>> slov = {'one':1, 'two':2, 'three':3} >>> mean_val = (lambda **slov: sum(slov.values()) / len(slov)) >>> mean_val(**slov) 2.0# tato funkce pracuje prostřednictvím proměnné **kwargs se slovníkem (viz výše) i s enticí pojmenovaných (keyword) hodnot: >>> mean_val(one=1, two=2, three=3) 2.0
Funkce
Funkce
Python disponuje obsáhlou řadou vestavěných matematických funkcí.
K řadě matematických funkcí a konstant získáme přístup importem modulu
>>> import math >>> math.exp(2)# --> e**2 7.38905609893065 >>> 3*math.pi 9.42477796076938
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.if x < y :elif x > y :else:
def vynos (p, r, *, t=10, n=12):
Vložte tento skript do souborudef cat_times (s,n): <zde zapište svůj kód>
>>> from caclr-ttimes import * >>> cat_times('Spam', 7) SpamSpamSpamSpamSpamSpamSpamChodí? Vyzkoušejte si funkci i pro jiné argumenty.