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-podzim-pondeli:turtle:0",
"title": "VlastnĂ funkce",
"html": "\n \n \n\n <h1>Funkce</h1>\n<p><a href=\"/2019/brno-podzim-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-podzim-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 "
}
}
}