![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
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 volaná pro objekt (instance) 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'
Funkce je pojmenované pořadí příkazů, které provádějí požadovanou operaci pro zadaný výčet parametrů. Tyto příkazy jsou deklarovány v těle funkce. 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ů je výčet hodnot, které je nutné funkci 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 (viz Kap. 5.10), která je nezávisle přístupná v prostředí konzoly.
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.
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 definice funkce bez parametrů (a bez docstringu):
def
novy_riadok():# uživatelská funkce bez parametru # volání 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í funkce print(), která bez argumentu vrátí prázdný řádek. Deklaraci funkce si uložíme do souboru s názvem emptyLines.py prostřednictvím editoru IDLE.
Definování nové funkce ještě nespustí její provádění. K tomu musíme funkci volat. Při volání 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é. Volání funkce zapíšeme rovněž do souboru emptyLines.py:
Výstupem tohoto programu v konzole IDLE je:# Text souboru emptyLines.py def
novy_riadok():# vrátí prázdný řádek print("First Line")# volání fce 'print' s argumentem novy_riadok()# volání nové funkce print("Second Line")# volání fce 'print' s argumentem
==== RESTART: F:\Codetest\HowTo\ch-03\emptyLines.py ====
First Line
# mezera je vytvořena funkcí novy_riadok()
Second Line
>>>
F:\Codetest\HowTo\ch-03> python emptyLines.py First Line Second Line F:\Codetest\HowTo\ch-03>
Kdybychom chtěli více místa mezi řádky, můžeme volat funkci opakovaně nebo si napsat funkci nazvanou tri_riadky, která vytiskne tři nové řádky:
def
tri_riadky():
novy_riadok()
novy_riadok()
novy_riadok()
Tuto funkci vložte do souboru emptyLines.py za definici funkce novy_riadok().
Funkce novy_riadok a tri_riadky() jsou náhodným příkladem složené funkce, o nichž budeme hovořit v odst. 3.10.
Speciálním případem složené funkce je funkce, která volá samu sebe. Taková funkce se nazývá rekurzivní a je popsána v odstavci 4.5 a 13.2.
Příkaz return
Výstup výsledku funkce jsme dosud zajišťovali funkcí print(..). Alternativní způsob poskytuje příkaz return, který je popsán v Kap. 4.4.
Funkce může vrátit pouze jeden objekt. Potřebujeme-li aby nám funkce vracela více hodnot, musíme je vložit do vhodného složeného typu - viz kap. 10.3
Většina funkcí je navrhována pro použití jedné nebo více vstupních hodnot. Hodnotám, které zadáváme při volání funkce, říkáme argumenty. Zadané hodnoty jsou uvnitř funkce přiřazeny k proměnným v závorce, jež souhrnně označujeme jako parametry.
Chceme-li například nalézt absolutní hodnotu čísla 5, můžeme použít vestavěnou funkci abs(n):
>>> abs(5) 5 >>> abs(-5) 5
V tomto příkladě jsou čísla 5 a -5 argumenty funkce abs(n) .
Vestavěná funkce max vrátí největší ze zadaných argumentů.
>>> max(7, 11) 11 >>> max("abakus")# pořadí je dáno přiřazenými kódovými čísly 'u' >>> max(3*11, 5**3, 512-9, 1024*0) 503
Výpis číselných hodnot lze také nahradit proměnnou typu list, tuple, set, ..., např:
>>> tup = (2.5, 4.2, 5) >>> print(sum(tup)) 11.7
U vestavěné funkce pow(x,y), se dvěma jednoduchými parametry, si musíme dávat pozor na pořadí zadávaných hodnot, neboť se v tomto případě jedná o takzvané poziční parametry (viz 3.3), případně argumenty:
>>> pow(2, 3) 8 >>> pow(3, 2) 9
Z pilné lenosti si ale můžeme vytvořit obdobnou funkci vlastní, u které přiřadíme počáteční hodnotu jednomu nebo oběma parametrům. Parametrům ve formátu jméno = hodnota
se říká pojmenované (keyword) parametry, případně parametry s počáteční hodnotou:
def mocnina(m, n=3):   print(m**n)
Přednastavenou hodnotu nemusíme při volání funkce uvádět, můžeme ji ale kdykoliv změnit:
>>> mocnina(2) 8 >>> mocnina(3, 2) # nebo polopaticky: mocnina(3, n=2) 9 >>>
Jak již bylo řečeno, názvům v závorce záhlaví funkce říkáme parametry, zatímco zadávaným hodnotám při volání funkce říkáme argumenty.
Kromě jednoduchých pozičních parametrů (např. x, y
) a klíčových parametrů (s přiřazenými počátečními hodnotami, např. n=3
) existují takzvané sběrné parametry *args a **kwargs. Sběrné parametry bývají také označovány jako variadické.
Poznámka: Podobné rozlišení jako u parametrů používáme i pro argumenty:
Sběrné parametry *args jsou určeny ke sběru pozičních argumentů, neboli enticových hodnot. Délka očekávané entice (tuple) není předem určena.
Sběrné parametry**kwargs jsou určeny ke sběru klíčových argumentů, neboli slovníkových párových hodnot . Délka očekávaného slovníku rovněž není předem určena.
Aby se interpret Pythonu v této přehršli možných parametrů a argumentů vyznal, je nezbytné dodržovat jistá pravidla. Cílem těchto pravidel je zajistit aby se při volání funkce množina argumentů řádně přiřadila k definované množině parametrů.
Zde je výtah z pravidel, uvedených v PEP 3102:
Uvedená pravidla mohou být zdrojem nekonečné zábavy. Zapište si funkci, kterou budete opakovaně volat pro různé argumenty parametrů (jednoduché poziční, jednoduché klíčové, sběrné poziční, sběrné klíčové):
def
hark(alfa, pi=3.14, *tup, **lib):
Vyzkoušejte si sami volání například pro tyto argumenty. Porovnávejte výsledky s uvedenými pravidly a čtěte případná chybová hlášení.
hark(5) hark(5, 4, 3, fi=8) hark(5, a=8, b=5) hark(5, pi)
Vhodným příkladem rozborky zadaných argumentů je i tato ukázka:
def
haro(alfa, b=12, c="gama"):
print("Volné poziční argumenty:", alfa)
print("Keyword argumenty:", b, c)
>>> haro("beta") Volné poziční argumenty: beta Keyword argumenty: 12 gama
Hvězdička *
, ve výčtu parametrů přikazuje, aby všechny parametry, které za ní následují, přijímaly pouze klíčové (keywords) argumenty. :
Tento prametr je vhodný pro kontrolu vstupních argumentůdef
foo(a, b, *, c, d):
>>> foo(2, 4, c="jin", "jan") SyntaxError: positional argument follows keyword argument >>> foo(2, 3, 4, c="jin", d="jan") TypeError: foo() takes 2 positional arguments but 3 positional arguments were given
Stejný vliv má sběrný parametr *args
:
def
foo(a, b, *arg, c, d):
>>> foo(1,2,3,4,5,c=6,d=7) 1 2 (3, 4, 5) 6 7
Sběrné parametry mohou posloužit jako šikovný 'vysavač' pro nadbytečně zadané argumenty:
def
eta(a, b, *c, **d):
>>> eta(2, 3, 4, 6) 5
Případně lze použít pouze prvky sběrného parametru:
def
sum_not_first(a, *bs): temp = sum(bs)parametr 'a' není zmíněn
>>> sum_not_first(1, 2, 3, 4)viz výtah z PEP 3102 ad 2 sum is: 9
Sběrný formát lze použít i při obsazování pozičních parametrů:
def
test_args(a, b, c):
>>> slova=("abra", "kadabra")
>>> test_args(2, *slova) # rozbalování argumentů z entice
Virbl: abrakadabraabrakadabra
Případně:
>>> pairs={"hola": "b", "hej": "c"}
>>> test_args(2, *pairs) # rozbalování argumentů ze slovníku
Virbl: holahejholahej
Celkový počet zadávaných hodnot zde musí být stejný jako počet pozičních parametrů.
Půvabná je rovněž tato ukázka rozbalení sběrných parametrů u slovníku:
def
moje_troje(a, b, c):
>>> a = {'a': "one", 'b': "two", 'c': "three" }typ 'dict' >>> moje_troje(*a) a b c >>> moje_troje(**a) one two three
Python 3.8 zavedl označení /
pro parametry, které mají být pouze poziční, například:
>Ve výpisu parametrů vidíme lomítko a hvězdičku. O hvězdičce již víme, že všechny argumenty za hvězdičkou musejí být pojmenované (keyword). Lomítko podobně požaduje aby všechny argumenty před lomítken byly pouze poziční. V našem případě mezilehlé argumenty pro c, d mohou být poziční i pojmenované. Obecně však platí, že poziční argumenty nesmějí přijít za pojmenovanými.def
f(a, b, /, c, d, *, e, f):
====== RESTART: F:\Codetest\HowTo\tremp.py ===== >>> 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: 'b'
Některé vestavěné funkce (pow(x,y), len(obj)) také připouštějí jenom poziční argumenty.
>>> len(obj='hello')TypeError: len() takes no keyword arguments
V právě probíraném kontextu působí "hvězdičkové" operátory *
a **
jako operátory balící, (packing) případně rozbalovací (unpacking). V následné ukázce vidíme, jak si v hromadném přiřazení (viz kap. 10.4) rozeberou proměnné a, b, c prvky seznamu (nebo entice):
>>> a, *b, c = [1, 2, 3, 4, 5, 6]# b = [2, 3, 4, 5] ... packing >>> print(a,b,c) 1 [2, 3, 4, 5] 6# vidíme, že *b produkuje seznam
Nebo jak si proměnná rozloží prvky stringu a vloží je do seznamu:
>>> *a, = "Řáholec""hvězdičková" proměnná musí mít formát entice >>> a ['Ř', 'á', 'h', 'o', 'l', 'e', 'c']# unpacking
Případně jak se při kompilaci rozbalí prvky seznamů:
my_first_dict = {"A": 1, "B": 2} my_second_dict = {"C": 3, "D": 4} my_merged_dict = {**my_first_dict, **my_second_dict}# unpacking
>>> my_merged_dict {'A': 1, 'B': 2, 'C': 3, 'D': 4}
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
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 * yjmé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)) >>> print(selekce) [8, 10, 11, 12, 13, 17, 21]
Funkce list, tuple, set - jsou funkce pro vytvoření seznamu, entice a setu.
Funkce filter - viz kapitola 4.10.
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) x = abs(3 - 11) + 10
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 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 v jiné funkci, například:
def suma(rad, b): print(area(rad) + b)# TypeError: NoneType + 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, jak uvidíme například v Kap. 4.4.
Ú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 (povinného) 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.
Následující funkce přijme seznam jako argument a vynásobí dvěma každou jeho položku. Pro pochopení skladby této funkce je nutné číst text 4.7, 4.8, 4.10 a předposlední odstavec 3.1.
def
double_stuff_m (a_list):# a_list je očekávaný seznam for
iin
range
(len
(a_list)): a_list[i] = a_list[i] * 2return
a_list
Vložíme-li double_stuff do souboru doubleStuff.py, můžeme si jej v IDLE vyzkoušet:
>>> lst = ["hi", 22, True] >>> double_stuff_m (lst) >>> lst ['hihi', 44, 2]# vstupní hodnota byla změněna >>>
Protože funkce mění vstupní hodnotu, je to funkce nečistá neboli modifikační.
Funkce, které přijmou objekt jako argument a změní jej při provádění, se nazývají modifikátory a změnám, které provedou na zadaném argumentu, se říká vedlejší účinky. Funkci s uvedenými vlastnostmi jsme si zapsali v předchozím odstavci.
Čistá funkce neprodukuje vedlejší účinky. Komunikuje s okolním programem pouze prostřednictvím argumentů, které nepřetváří a prostřednictvím výstupní hodnoty. Zde je double_stuff_p jako čistá funkce:
def double_stuff_p ( a_list ): new_list = []# zárodek kopie původního seznamu for value in a_list: new_list += [2*value] return new_list
Funkce double_stuff_p nemění své argumenty:
>>> 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
Chceme-li, aby čistá funkce double_stuff_p změnila seznam lst, přiřadíme mu výstup z čisté funkce:
>>> lst = double_stuff_p (lst) >>> lst ['hihi', 44, 2]
Všechno, co může být provedeno pomocí modifikátorů, může být také provedeno čistými funkcemi. Některé programovací jazyky znají vlastně jenom čisté funkce. 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ě, modifikátory jsou někdy vhodné a v některých případech jsou programy s čistými funkcemi méně výkonné.
Doporučujeme tedy používat čisté funkce kdykoli je to účelné, a k modifikátorům se uchylovat jen při jejich zjevné výhodnosti. Tento přístup lze označit jako funkcionální programovací styl.
Funkce jsou objekty, opatřené jménem. Na jednu funkci může odkazovat více jmen, stejně jako může jedno jméno postupně odkazovat na různé funkce (či jiné objekty).
Funkce mohou být zadány jako argumenty jiným funkcím a mohou být výstupem jiných funkcí:
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 doto je volána třikrát pro stejný číselný argument a pro tři různá jména čistých funkcí. Tato funkce je rovněž čistá funkce, navíc je to funkce vyššího řádu, neboť jako argument akceptuje jinou funkci.
Jiný příklad funkce vyššího řádu:
def
print_haf():# prostá funkce print('Haff')def
do_twice(miau):# miau zde zastupuje název funkce * miau(); miau() do_twice(print_haf)# argumentem je název fce print_haf
* protože se stejné slovo vyskytuje v těle funkce jako jméno funkce
Při volání funkce do_twice je parametr 'miau' nahrazen argumentem, jímž je název funkce print_haf.
====== RESTART: F:/Codetest/HowTo/ch-03/doTwice.py ====== Haff Haff
Vnořené nebo 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 bližším 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()# vnitřní funkce musí být volána z vnější fce
>>> outer_var() outer = 10# lok. proměnná vnější fce (2) zde není stíněna inner = 14# lok. proměnná vnitřní fce (6) stíní lok. proměnnou vnější fce (2) Obě funkce mají přístup ke globální proměnné y
Pokud bychom potřebovali aby hodnota lokální proměnné vnitřní funkce byla přístupná i z prostoru vnější funkce, deklarujeme ji jako nonlocal
:
a = "piroh"def
outer_fce(): a = 5# lokální proměnná vnější fce def
inner_fce():nonlocal
a# deklarace 'nelokálnosti' a = 10# lokální proměnná vnitřní fce # volání vnitřní fce # příkaz vnější fce outer_fce()# volání vnější fce print("Mimo fce: a = ", a)
Vnitřní fce: a = 10 Vnější fce: a = 10 Mimo fce: a = piroh
Kdybychom vypustili deklaraci nelokálnosti (nonlocal a), dostali bychom tento výstup:
Vnitřní fce: a = 10 Vnější fce: a = 5 Mimo fce: a = piroh
Kdybychom zrušili všechny proměnné 'a' uvnitř funkcí, dostali bychom tento výstup:
Vnitřní fce: a = piroh# Obě funkce 'vidí' globální proměnnou a='piroh' Vnější fce: a = piroh# Dtto Mimo fce: a = piroh
Kdybychom místo deklarace nonlocal použili ve vnitřní funkci deklaraci global
, dostali bychom tento výstup:
Vnitřní fce: a = 10# Deklarace 'global' je ve vnitřní funkci Vnější fce: a = 5 Mimo fce: a = 10
Kdybychom deklaraci global použili ve vnější funkci, dostali bychom tento výstup:
Vnitřní fce: a = 10# Deklarace 'global' je ve vnější funkci Vnější fce: a = 5 Mimo fce: 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# první reference 's' s = "zlatíčko"# druhá reference 's' s = "Hordubal" ule() print("three: s = ", s)# třetí reference 's'
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".
one: s = Hordubal two: s = zlatíčko three: s = zlatíčko
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. Deklarace proměnné v rámci vnější funkce může mít formu parametru:
def
num_outer(x):# vnější funkce def
num_inner(y):# vnitřní funkce return
x * y# příkaz vnitřní funkce return
num_inner# příkaz vnější funkce
Volání vnější funkce vrací vnitřní funkci pro konkretní argument vnější funkce.
>>> num_outer(10) <function num_outer.<locals>.num_inner at 0x03DE2028>
Volání klauzury lze provést jednou 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 pevně vázaná na hodnotu x=12, pročež lze funkci num_outer případně smazat a používat jen volání vnitřní funkce:
>>> del(num_outer) huk(5)# x = 12, y = 5 60
Vnější funkce s klasickou 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'nebo: bat = pat(); bat() => Jsem 'pat'
Kromě toho, že můžeme funkci zadat jako argument jiné funkci (jak popsáno v předchozím odstavci), 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.
Jako první příklad složené funkce jsme viděli funkci tri_riadky, která ve svém těle volala funkci novy_riadok. Rekurzivní funkci poznáme v kapitole 4.5.
Jako další příklad prosté složené funkce 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 (podrobný rozvoj této funkce viz kap. 5.2 Rozvoj programu), 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 # nezbytné pro přístup k funkci 'sqrt' a ke konstantě 'pi' def distance(x1,y1, x2,y2): return math.sqrt((x2-x1)**2 + (y2-y1)**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í.
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 3.8.
Dekorátory jsou funkce s klauzurami (s oblibou nazývanými 'wrapper'), které upravují výstup jiné (dekorované) funkce. Jak uvidíme později, klauzury nejsou povinné - viz zde . Dekorátor použije nepřímý odkaz na dekorovanou funkci jako parametr a upraví její výstup.
Mechanizmus dekorátorů vychází z toho, že funkce může být zadána jako argument, definována uvnitř jiné funkce, vrácena jinou funkcí a být přiřazena k proměnné.
Dekorátorová funkce:
def decorator(func):# dekorátor def wrapper():# klauzura print("Před spuštěním ovečky") func()# interní volání ovečky print("Po spuštění ovečky") return wrapperDekorovaná funkce:
# říkejme jí ovečka def decorated_function():# příklad ovečky print("Ovečka se pěkně pase.")
Nezbytné propojení obou funkcí se provede přiřazením decorated_function = decorator(decorated_function)
, které je zadáno buď
decorated_function = decorator(decorated_function)nebo třeba ovečka = decorator(decorated_function)
Invokací decorated_function() nebo ovečka() se volá dekorátorová funkce, jejímž argumentem je dekorovaná funkce:
>>> ovečka() Před spuštěním ovečky Ovečka se pěkně pase. Po spuštění ovečky
@decorator# <== decorated_function = decorator(decorated_function) def decorated_function(): print("Ovečka se pěkně pase.")
>>> decorated_function()# vlastní název zde nelze použít Před spuštěním ovečky Ovečka se pěkně pase. Po spuštění ovečky
Pro pochopení popisovaného mechanizmu je vhodné připomenout, že při volání dekorované funkce se nejprve provádí tělo dekorátorové funkce. Dekorovaná funkce je volaná z těla dekorátorové funkce.
Existujícím dekorátorem lze dekorovat jakoukoli vhodnou funkci (i metodu třídy - viz kap. 12.12).
@decorator def beran(): print("Berany, berany duc!")
>>> beran() Před spuštěním ovečky Berany, berany duc! Po spuštění ovečky
Rovněž je možné jednu funkci dekorovat více dekorátory:
def decorator(ovce): def wrapper(): print("Před spuštěním ovečky") ovce() print("Po spuštění ovečky") return wrapper def lines(ovce): def wrapper(): print("- " * 12) ovce() print("- " * 12) return wrapper @decorator @lines def beran(): print("Berany, berany, duc!")
Zpracování kódu začíná prvním dekorátorem nad názvem dekorované funkce:
>>> beran() Před spuštěním ovečky - - - - - - - - - - - - Berany, berany, duc! - - - - - - - - - - - - Po spuštění ovečky
Jako ukázku si uvedeme dekorátor, který zabrání dělení nulou. Povšimněte si shodných parametrů u klauzury, odkazu na ovečku i u vlastní ovečky (dekorované funkce).
def smart_divide(func): def wrapper(a,b):# klauzura if b == 0: print("Nulou nelze dělit!") return# uplatní se jen při b == 0 return func(a,b)# nezbytný odkaz na ovečku return wrapper @smart_divide def divide(a,b): return a/b
>>> divide(5,7) 0.7142857142857143 >>> divide(5,0) Nulou nelze dělit!
Při práci s argumenty je vhodné používat sběrné parametry *args, *kwargs. Parametry vnitřní funkce (klauzury) wrapper musí být kompatibilní s parametry invokované funkce:
def logging(func): def wrapper(*args , **kwargs ): print('Příchozí argumenty',args ,kwargs ) output = func(*args , **kwargs ) print("Výstup", output) return wrapper @logging def sumace(a, b, c,vasil =0): return a+b+c+vasil
>>> sumace(1, 2, 3,vasil =5)# viz Poznámka Příchozí argumenty (1, 2, 3) {'vasil': 5} Výstup 11 >>> sumace(1 ,2, 3) Příchozí argumenty (1, 2, 3) {} Výstup 6
Při volání funkce je nutné zachovat počet pozičních argumentů a počet a název klíčového argumentu. Klíčový argument může chybět, poziční argumenty nikoliv. Vyzkoušejte si to.
Vlastní dekorátorová funkce (dekorátor) může mít pouze jeden parametr, jímž je nepřímý odkaz na dekorovanou funkci. Případný další parametr se připojuje k další vnější funkci (kvazi dekorátor). Propojení přiřazením se v tomto případě vytváří mezi dekorovanou funkcí a kvazidekorátorem.
Příklad plně vybaveného (kvazi) dekorátoru, jehož účelem je poskytnout argument dekorované funkci
def anonce(n):# kvazi dekorátor def decorator(func):# dekorátor def wrapper(*args, **kwargs):# klauzura result = func(*args, **kwargs)# volání ovečky return result + n**n# úprava ovečky return wrapper return decorator @anonce(2)# argument je součástí deklarace def add(x, y): return x + y
>>> add(5,7) 16
Dekorátorovou funkci tvoří pouze kvazidekorátor a dekorátor:
def call(*argv, **kwargs): # kvazidekorátor def dekor(func): # dekorátor return func(*argv, **kwargs) # invokace ovečky return dekor # Using the decorator function @call(5) def add(x, y): return x + y
Systémový dekorátor @wraps umožňuje přístup k introspektivním atributům funkce. Tento dekorátor importujeme z modulu functools. Atribut .num_calls vrací počet volání funkce:
from functools import wraps def counter(func): @wraps(func)# dekorace importovaným dekorátorem def wrapper(*args, **kwargs): wrapper.num_calls += 1# počítadlo print(f"Call {wrapper.num_calls} of# viz pozn. 1 {func.__name__!r}:")# viz pozn. 2 return func(*args, **kwargs) wrapper.num_calls = 0 return wrapper @counter# dekorace vlastní funkcí def add(x, y): "Aplikován dekorátor 'wraps'" print(x + y)
Poznámka č. 1: Písmeno f uvádí takzv. f-string - viz kap. 6.17.
Poznámka č. 2: Písmeno !r označuje druh konverze - viz kap. 6.16, sektor !conversion.
>>> add(5,7) Call 1 of 'add': 12 >>> add(5,7) Call 2 of 'add': 12 >>> add.__name__ ==> 'add' >>> add.__doc__ ==> "Aplikován dekorátor 'wraps'" >>> add.__module__ ==> '__main__'
Pro výše uvedené vlastnosti je doporučeníhodné používat importovaný dekorátor @wraps v hojné míře.
Jmenný prostor (namespace) je výčet objektů (proměnná, funkce, třída, soubor, modul) ve formě slovníku (dict), který pro každé jméno obsaženého objektu uvádí jeho identifikační číslo.
V každém okamžiku při běhu programu existuje několik automaticky vytvářených jmenných prostorů.
Jmenné prostory tvoří hierarchickou strukturu. Nejvýše postaveným je jmenný prostor built-in
, v němž jsou obsažena všechna jména funkcí a metod, přímo přístupná bez importu a tečkové notace a který se aktivuje při spuštění Pythonu a zaniká při ukončení seance Pythonu.
O stupeň nižší úroveň má vnořený globální JP
, tvořený prostorem aktuálně načteného skriptu, případně prostorem otevřené konzoly Pythonu.
Lokální JP
tvoří funkce a metody, (případně moduly, importované příkazem import <module>). Tyto JP vznikají při volání funkce či metody a končí s koncem jejich provedení nebo vyvoláním výjimky.
Prostor nejnižší úrovně tvoří vnitřní JP
vnořených funkcí.
Skutečnost jmenného prostoru je lexikálně daná napsaným a zapsaným kódem. Kromě toho existuje pro jednotlivá jména jmenného prostoru termín scope
, což je množina příslušných JP, postupně prohledávaných při hledání daného jména.
Při hledání jména začíná Python prostředím, ve kterém bylo jméno invokováno a postupuje směrem k hierarchicky výše postavenému prostředí, konče případně až v prostředí built-in. Důsledkem této vlastnosti je například to, že funkce může volat jméno, deklarované vně funkce - avšak jména, deklarovaná uvnitř funkce, nejsou zvnějšku přístupná.
# namespace.py # globální jmenný prostor skriptu x,y = 10,20# globální proměnné x, y def
outer():# vnější funkce z = "'erteple'"# vnější lokální proměnná def
inner():# vnitřní funkce x = 30# vnitřní lokální proměnná # vniřní invokace vnitřní lok. proměnné # vniřní invokace globální proměnné # vniřní invokace vnější lok. proměnné inner()# lokální invokace vnitřní funkce outer()# globální invokace vnější funkce
print
(f"x is {x}"): Argument příkazu k tisku používá takzvaný formátovaný literál řetězce - viz 6.17.
===== RESTART: F:/Codetest/HowTo/ch-07/namespace.py ===== x is 30# globální x je zastíněno vnitřním x y is 20# globální y není zastíněno, proto je dostupné z is 'erteple'# lokální z je dostupné z vnitřního JP >>> print(z)# lokální z není dostupné z globálního JP NameError: name 'z' is not defined
Lokální proměnná může stínit globální proměnnou, závisí však na pořadí deklarace proměnné a její invokace:
# hiddenVar.py s = "Pijme pivo s bobkem,"# globální proměnná def f(): print(s) s = "jezme bedrník."# lokální proměnná f()
Při tomto pořadí dochází k následující chybě:
UnboundLocalError: local variable 's' referenced before assignment
Zrušíme-li deklaraci vnitřní proměnné (
Pijme pivo s bobkem,# k bedrníku se však nedostanem
Přemístíme-li proměnnou (
jezme bedrník.
Protože modul (prostý soubor s příponou .py) také vymezuje svůj vlastní jmenný prostor, můžeme stejné jméno použít v různých modulech aniž bychom vyvolali problém s jeho příslušností.
# module1.py question = "Co má vliv na nesmrtelnost brouků?" answer = 42
# module2.py question = "What is your quest?" answer = "To seek the holy grail."
Můžeme nyní oba moduly (soubory ~.py) importovat a zajistit si v každém přístup k proměnným question a answer :
>>> import module1 >>> import module2 >>> print(module1.question) Co má vliv na nesmrtelnost brouků?? >>> print(module2.question) What is your quest? >>> print(module1.answer) 42 >>> print(module2.answer) To seek the holy grail. >>>
Kdybychom byli použili idiom from module1 import* a from module2 import*, dospěli bychom ke jmenné kolizi, neboť by se nám ve jmenném prostoru konzoly interpreta Pythonu ocitla stejná jména z různých modulů.
Označení __main__
je jméno prostředí, ve kterém probíhá exekuce "top-level" skriptu. Hodnota atributu __name__ je nastavena na hodnotu '__main__', je-li modul načten ve skriptu nebo v interaktivní konzole.
Způsob provedení importovaného skriptu je řízen podmínkou na konci aktuálního skriptu:
<>if __name__ == "__main__":# execute only if run as a script main()
|
|
|
|
|
|
|
|
|
|
|
|
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.
def vynos(p, r, *, t=10, n=12):
def cat_times(s,n): <zde zapište svůj kód>Vložte tento skript do souboru catTimes.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 catTimes import * >>> cat_times('Spam', 7) SpamSpamSpamSpamSpamSpamSpamChodí? Vyzkoušejte si funkci i pro jiné argumenty.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |