Dříve jsme volaly funkce, které napsal někdo jiný:
print('Ahoj světe!')
Dnes si ukážeme, jak psát funkce vlastní.
Pokud umíš if
a for
– s jednořádkovou hlavičkou a odsazeným tělem příkazu –
neměl by ti zápis funkce připadat nijak zvláštní:
def obvod_obdelnika(sirka, vyska):
"Vrátí obvod obdélníka daných rozměrů"
return 2 * (sirka + vyska)
print(obvod_obdelnika(4, 2))
Jak to funguje?
Funkce se definuje příkazem def
, za nějž napíšeš jméno funkce,
pak do závorky seznam parametrů, které funkce bere, a pak dvojtečku.
Potom následuje odsazené tělo funkce – příkazy, které funkce provádí. Tělo může začít dokumentačním řetězcem, který popisuje, co funkce dělá.
Příkazem return
pak můžeš z funkce vrátit nějakou hodnotu.
Tělo funkce může mít více příkazů – včetně podmínek, cyklů a podobně. Následující procedura třeba vypíše skóre daného hráče a k tomu hlášku:
def napis_hlasku(nazev, skore):
"Popíše skóre. Název má být přivlastňovací přídavné jméno."
print(nazev, 'skóre je', skore)
if skore > 1000:
print('Světový rekord!')
elif skore > 100:
print('Skvělé!')
elif skore > 10:
print('Ucházející.')
elif skore > 1:
print('Aspoň něco')
else:
print('Snad příště.')
napis_hlasku('Tvoje', 256)
napis_hlasku('Protivníkovo', 5)
Při volání funkce se hodnoty, se kterými funkci
zavoláš, přiřadí jednotlivým parametrům.
Takže když zavoláš třeba napis_hlasku('Tvoje', 256)
,
můžeš si představit, že funkce dělá následující:
nazev = 'Tvoje'
skore = 256
print(nazev, 'skóre je', skore)
if skore > 1000:
... # atd.
Speciální příkaz return
, který jde použít jenom ve funkcích,
ukončí funkci a vrátí danou hodnotu ven z funkce.
Chová se tedy trochu jako break
, jen místo cyklu opouští celou funkci.
Podobně jako break
se dá použít v případech, kdy potřebuješ od uživatele
dostat odpověď – a opakuješ dotaz tak dlouho, dokud požadovanou odpověď
nedostaneš.
Třeba, chceš-li odpověď „ano“ nebo „ne“:
def ano_nebo_ne(otazka):
"Vrátí True nebo False podle odpovědi uživatele"
while True:
odpoved = input(otazka)
if odpoved == 'ano':
return True
elif odpoved == 'ne':
return False
print('Nerozumím! Odpověz "ano" nebo "ne".')
# Příklad použití
if ano_nebo_ne('Chceš si zahrát hru? '):
print('OK! Ale napřed si ji musíš naprogramovat.')
else:
print('Škoda.')
Stejně jako if
nebo break
je return
příkaz, ne funkce.
Kolem „své“ hodnoty nepotřebuje závorky.
Zkus napsat funkci, která vrátí obsah elipsy daných rozměrů. Příslušný vzoreček je A = πab, kde a a b jsou délky os.
Funkci zavolej a výsledek vypiš.
Předchozí program se dá napsat i s procedurou místo funkce:
from math import pi
def obsah_elipsy(a, b):
print('Obsah je', pi * a * b) # Pozor, `print` místo `return`!
obsah_elipsy(3, 5)
Program takhle funguje, ale přichází o jednu z hlavních výhod funkcí:
možnost vrácenou hodnotu použít i jinak jež jen v print
.
Funkci, která vrací výsledek, můžeš použít v dalších výpočtech:
def objem_eliptickeho_valce(a, b, vyska):
return obsah_elipsy(a, b) * vyska
print(objem_eliptickeho_valce(3, 5, 3))
... ale s procedurou, která výsledek přímo vypíše, by to nešlo.
Další důvod, proč hodnoty spíš vracet než vypisovat, je ten, že jedna funkce se
dá použít v různých situacích.
Proceduru s print
by nešlo rozumně použít tehdy, když nás příkazová
řádka vůbec nezajímá – třeba v grafické hře, webové aplikaci, nebo pro ovládání
robota.
Podobně je to se vstupem: když použiju v rámci své funkce input
, bude se
moje funkce dát použít jen v situacích, kdy je u počítače klávesnice a za ní
člověk.
Proto je lepší funkcím potřebné informace předávat jako argumenty
a input
(nebo textové políčko či měření z čidla robota) nemít ve funkci,
ale vně:
from math import pi
def obsah_elipsy(a, b):
"""Vrátí obsah elipsy s poloosami daných délek"""
# Jen samotný výpočet:
return pi * a * b
# print a input jsou "venku":
x = float(input('Zadej délku poloosy 1: '))
y = float(input('Zadej délku poloosy 2: '))
print('Obsah je', obsah_elipsy(x, y))
Samozřejmě existují výjimky: procedura která přímo vytváří textový výpis
může používat print
; funkce která načítá textové informace zase input
.
Když ale funkce něco počítá, nebo když si nejsi jistá,
je dobré ve funkci print
ani input
nemít.
Když funkce neskončí příkazem return
,
automaticky se vrátí hodnota None
.
Je to hodnota zabudovaná přímo do Pythonu, podobně jako True
nebo False
,
a znamená „nic“.
def nic():
"Tahle funkce nic nedělá"
print(nic())
{ "data": { "sessionMaterial": { "id": "session-material:2019/brno-jaro-2019-pondeli:turtle:0", "title": "VlastnĂ funkce", "html": "\n \n \n\n <h1>Funkce</h1>\n<p><a href=\"/2019/brno-jaro-2019-pondeli/beginners/functions/\">Dříve</a> jsme\nvolaly funkce, které napsal někdo jiný:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Ahoj světe!'</span><span class=\"p\">)</span>\n</pre></div><p>Dnes si ukážeme, jak psát funkce vlastní.</p>\n<p>Pokud umíš <code>if</code> a <code>for</code> – s jednořádkovou hlavičkou a odsazeným tělem příkazu –\nneměl by ti zápis funkce připadat nijak zvláštní:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">obvod_obdelnika</span><span class=\"p\">(</span><span class=\"n\">sirka</span><span class=\"p\">,</span> <span class=\"n\">vyska</span><span class=\"p\">):</span>\n <span class=\"s2\">"Vrátí obvod obdélníka daných rozměrů"</span>\n <span class=\"k\">return</span> <span class=\"mi\">2</span> <span class=\"o\">*</span> <span class=\"p\">(</span><span class=\"n\">sirka</span> <span class=\"o\">+</span> <span class=\"n\">vyska</span><span class=\"p\">)</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">obvod_obdelnika</span><span class=\"p\">(</span><span class=\"mi\">4</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">))</span>\n</pre></div><p>Jak to funguje?</p>\n<p>Funkce se <em>definuje</em> příkazem <code>def</code>, za nějž napíšeš jméno funkce,\npak do závorky seznam <em>parametrů</em>, které funkce bere, a pak dvojtečku.</p>\n<p>Potom následuje odsazené <em>tělo funkce</em> – příkazy, které funkce provádí.\nTělo může začít <em>dokumentačním řetězcem</em>, který popisuje, co funkce dělá.</p>\n<p>Příkazem <code>return</code> pak můžeš z funkce <em>vrátit</em> nějakou hodnotu.</p>\n<p>Tělo funkce může mít více příkazů – včetně podmínek, cyklů a podobně.\nNásledující procedura třeba vypíše skóre daného hráče a k tomu hlášku:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">napis_hlasku</span><span class=\"p\">(</span><span class=\"n\">nazev</span><span class=\"p\">,</span> <span class=\"n\">skore</span><span class=\"p\">):</span>\n <span class=\"s2\">"Popíše skóre. Název má být přivlastňovací přídavné jméno."</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">nazev</span><span class=\"p\">,</span> <span class=\"s1\">'skóre je'</span><span class=\"p\">,</span> <span class=\"n\">skore</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">skore</span> <span class=\"o\">></span> <span class=\"mi\">1000</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Světový rekord!'</span><span class=\"p\">)</span>\n <span class=\"k\">elif</span> <span class=\"n\">skore</span> <span class=\"o\">></span> <span class=\"mi\">100</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Skvělé!'</span><span class=\"p\">)</span>\n <span class=\"k\">elif</span> <span class=\"n\">skore</span> <span class=\"o\">></span> <span class=\"mi\">10</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Ucházející.'</span><span class=\"p\">)</span>\n <span class=\"k\">elif</span> <span class=\"n\">skore</span> <span class=\"o\">></span> <span class=\"mi\">1</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Aspoň něco'</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Snad příště.'</span><span class=\"p\">)</span>\n\n<span class=\"n\">napis_hlasku</span><span class=\"p\">(</span><span class=\"s1\">'Tvoje'</span><span class=\"p\">,</span> <span class=\"mi\">256</span><span class=\"p\">)</span>\n<span class=\"n\">napis_hlasku</span><span class=\"p\">(</span><span class=\"s1\">'Protivníkovo'</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">)</span>\n</pre></div><p>Při volání funkce se hodnoty, se kterými funkci\nzavoláš, přiřadí jednotlivým parametrům.\nTakže když zavoláš třeba <code>napis_hlasku('Tvoje', 256)</code>,\nmůžeš si představit, že funkce dělá následující:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">nazev</span> <span class=\"o\">=</span> <span class=\"s1\">'Tvoje'</span>\n<span class=\"n\">skore</span> <span class=\"o\">=</span> <span class=\"mi\">256</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">nazev</span><span class=\"p\">,</span> <span class=\"s1\">'skóre je'</span><span class=\"p\">,</span> <span class=\"n\">skore</span><span class=\"p\">)</span>\n<span class=\"k\">if</span> <span class=\"n\">skore</span> <span class=\"o\">></span> <span class=\"mi\">1000</span><span class=\"p\">:</span>\n <span class=\"o\">...</span> <span class=\"c1\"># atd.</span>\n</pre></div><h2>Vracení</h2>\n<p>Speciální příkaz <code>return</code>, který jde použít jenom ve funkcích,\n<em>ukončí</em> funkci a vrátí danou hodnotu ven z funkce.</p>\n<p>Chová se tedy trochu jako <code>break</code>, jen místo cyklu opouští celou funkci.</p>\n<p>Podobně jako <code>break</code> se dá použít v případech, kdy potřebuješ od uživatele\ndostat odpověď – a opakuješ dotaz tak dlouho, dokud požadovanou odpověď\nnedostaneš.\nTřeba, chceš-li odpověď „ano“ nebo „ne“:</p>\n<ul>\n<li>Takhle se zjišťuje odpověď ano (Pravda) nebo ne (Nepravda) na danou <em>otázku</em>:<ul>\n<li>Pořád dokola:<ul>\n<li>Zeptej se na <em>otázku</em>; zapamatuj si <em>odpověď</em>.</li>\n<li>Je-li odpověď „ano“:<ul>\n<li>Výsledek je Pravda. Hotovo; dál nepokračuj.</li>\n</ul>\n</li>\n<li>Jinak, je-li odpověď „ne“:<ul>\n<li>Výsledek je Nepravda. Hotovo; dál nepokračuj.</li>\n</ul>\n</li>\n<li>Pouč uživatele, ať odpoví „ano“ nebo „ne“.\n<br><em>(a zkus to znovu – viz „Pořád dokola“)</em></li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">ano_nebo_ne</span><span class=\"p\">(</span><span class=\"n\">otazka</span><span class=\"p\">):</span>\n <span class=\"s2\">"Vrátí True nebo False podle odpovědi uživatele"</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"n\">odpoved</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"n\">otazka</span><span class=\"p\">)</span>\n <span class=\"k\">if</span> <span class=\"n\">odpoved</span> <span class=\"o\">==</span> <span class=\"s1\">'ano'</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"bp\">True</span>\n <span class=\"k\">elif</span> <span class=\"n\">odpoved</span> <span class=\"o\">==</span> <span class=\"s1\">'ne'</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"bp\">False</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Nerozumím! Odpověz "ano" nebo "ne".'</span><span class=\"p\">)</span>\n\n<span class=\"c1\"># Příklad použití</span>\n<span class=\"k\">if</span> <span class=\"n\">ano_nebo_ne</span><span class=\"p\">(</span><span class=\"s1\">'Chceš si zahrát hru? '</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'OK! Ale napřed si ji musíš naprogramovat.'</span><span class=\"p\">)</span>\n<span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Škoda.'</span><span class=\"p\">)</span>\n</pre></div><div class=\"admonition note\"><p>Stejně jako <code>if</code> nebo <code>break</code> je <code>return</code> <em>příkaz</em>, ne funkce.\nKolem „své“ hodnoty nepotřebuje závorky.</p>\n</div><p>Zkus napsat funkci, která vrátí obsah elipsy\ndaných rozměrů.\nPříslušný vzoreček je <var>A</var> = π<var>a</var><var>b</var>,\nkde <var>a</var> a <var>b</var> jsou délky os.</p>\n<p>Funkci zavolej a výsledek vypiš.</p>\n<div class=\"solution\" id=\"solution-0\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2019/brno-jaro-2019-pondeli/beginners/def/index/solutions/0/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">math</span> <span class=\"kn\">import</span> <span class=\"n\">pi</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n <span class=\"k\">return</span> <span class=\"n\">pi</span> <span class=\"o\">*</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Obsah elipsy s osami 3 cm a 5 cm je'</span><span class=\"p\">,</span> <span class=\"n\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">),</span> <span class=\"s1\">'cm2'</span><span class=\"p\">)</span>\n</pre></div>\n </div>\n</div><h3>Vrátit nebo vypsat?</h3>\n<p>Předchozí program se dá napsat i s procedurou místo funkce:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">math</span> <span class=\"kn\">import</span> <span class=\"n\">pi</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Obsah je'</span><span class=\"p\">,</span> <span class=\"n\">pi</span> <span class=\"o\">*</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span><span class=\"p\">)</span> <span class=\"c1\"># Pozor, `print` místo `return`!</span>\n\n<span class=\"n\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">)</span>\n</pre></div><p>Program takhle funguje, ale přichází o jednu z hlavních výhod funkcí:\nmožnost vrácenou hodnotu použít i jinak jež jen v <code>print</code>.</p>\n<p>Funkci, která vrací výsledek, můžeš použít v dalších výpočtech:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">objem_eliptickeho_valce</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">,</span> <span class=\"n\">vyska</span><span class=\"p\">):</span>\n <span class=\"k\">return</span> <span class=\"n\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">vyska</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">objem_eliptickeho_valce</span><span class=\"p\">(</span><span class=\"mi\">3</span><span class=\"p\">,</span> <span class=\"mi\">5</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">))</span>\n</pre></div><p>... ale s procedurou, která výsledek přímo vypíše, by to nešlo.</p>\n<p>Další důvod, proč hodnoty spíš vracet než vypisovat, je ten, že jedna funkce se\ndá použít v různých situacích.\nProceduru s <code>print</code> by nešlo rozumně použít tehdy, když nás příkazová\nřádka vůbec nezajímá – třeba v grafické hře, webové aplikaci, nebo pro ovládání\nrobota.</p>\n<p>Podobně je to se vstupem: když použiju v rámci své funkce <code>input</code>, bude se\nmoje funkce dát použít jen v situacích, kdy je u počítače klávesnice a za ní\nčlověk.\nProto je lepší funkcím potřebné informace předávat jako argumenty\na <code>input</code> (nebo textové políčko či měření z čidla robota) nemít ve funkci,\nale vně:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">math</span> <span class=\"kn\">import</span> <span class=\"n\">pi</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n <span class=\"sd\">"""Vrátí obsah elipsy s poloosami daných délek"""</span>\n <span class=\"c1\"># Jen samotný výpočet:</span>\n <span class=\"k\">return</span> <span class=\"n\">pi</span> <span class=\"o\">*</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span>\n\n<span class=\"c1\"># print a input jsou "venku":</span>\n<span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">'Zadej délku poloosy 1: '</span><span class=\"p\">))</span>\n<span class=\"n\">y</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">'Zadej délku poloosy 2: '</span><span class=\"p\">))</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Obsah je'</span><span class=\"p\">,</span> <span class=\"n\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">x</span><span class=\"p\">,</span> <span class=\"n\">y</span><span class=\"p\">))</span>\n</pre></div><p>Samozřejmě existují výjimky: procedura která přímo vytváří textový výpis\nmůže používat <code>print</code>; funkce která načítá textové informace zase <code>input</code>.\nKdyž ale funkce něco <em>počítá</em>, nebo když si nejsi jistá,\nje dobré ve funkci <code>print</code> ani <code>input</code> nemít.</p>\n<h2>None</h2>\n<p>Když funkce neskončí příkazem <code>return</code>,\nautomaticky se vrátí hodnota <code>None</code>.</p>\n<p>Je to hodnota zabudovaná přímo do Pythonu, podobně jako <code>True</code> nebo <code>False</code>,\na znamená „nic“.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">nic</span><span class=\"p\">():</span>\n <span class=\"s2\">"Tahle funkce nic nedělá"</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">nic</span><span class=\"p\">())</span>\n</pre></div>\n\n\n " } } }