Cvičení č. 7 rozšířené o poznámky ze cvičení a řešení některých příkladů (ZS 2024/25)¶

V minulém díle jste viděli...¶

Podmíněný příkaz if s volitelnými bloky elif a else:

In [1]:
a = 0
if a > 0:
    print("číslo je kladné")
elif a == 0:
    print("číslo se rovná 0")
else:
    print("číslo není kladné")
číslo se rovná 0

Podmíněný výraz:

In [2]:
x = -1
y = 2
z = x if x + y > 0 else y

Příkaz while:

In [3]:
n = 0
while n <= 4:
    print(n)
    n += 1
0
1
2
3
4

Příkazy break a continue sloužící pro podrobnější kontrolu provádění cyklů.

Příkaz for využívající funkci range jako zdroj hodnot:

In [1]:
for n in range(100, 20, -2):
    print(n, end = " ")
100 98 96 94 92 90 88 86 84 82 80 78 76 74 72 70 68 66 64 62 60 58 56 54 52 50 48 46 44 42 40 38 36 34 32 30 28 26 24 22 

Strukturování programu: funkce¶

Funkce jsou patrně nejdůležitější prvek programovacích jazyků, který slouží ke strukturování programu. Umožňují totiž pojmenovat určitou skupinu příkazů, které potom lze použít opakovaně s různými vstupními parametry. Pokud tedy programujeme algoritmus pro řešení nějaké úlohy, můžeme jej umístit do samostatné funkce, abychom při opakovaném řešení úlohy s jinými daty nemuseli znovu programovat ten samý algoritmus. Funkce tedy programátorům umožňují dodržovat princip don't repeat yourself (DRY).

Příkaz def – definice funkce¶

Příkaz def je složený příkaz, který se skládá z hlavičky a těla funkce. Hlavička se skládá z klíčového slova def, názvu funkce, seznamu parametrů uvnitř kulatých závorek a závěrečné dvojtečky. Tělo funkce následující za dvojtečkou tvoří blok příkazů se správným odsazením dle univerzálního syntaktického pravidla.

Zápis se nejlépe vysvětlí pomocí příkladů. Následující funkce print_demo nemá žádný parametr:

In [5]:
# definice funkce: hlavička a tělo
# Tato funkce nemá žádný parametr a vrací hodnotu None
def print_demo():
    print("Byla spuštěna funkce 'print_demo'.")
In [6]:
# příkaz definice funkce je podobný jako příkaz definice proměnné: 
# také nic nevypíše, ale vytvoří se objekt reprezentující funkci a proměnná (zde print_demo),
# která na objekt odkazuje
x = 5+7

# ukázka: debugger

Funkce print_demo1 má jeden parametr:

In [7]:
# Tato funkce má jeden parametr a vrací hodnotu None
def print_demo1(param):
    print("Hodnota parametru je", param)

Funkce print_demo2 má dva parametry:

In [8]:
# Tato funkce má dva parametry a vrací hodnotu None
def print_demo2(param1, param2):
    print("Hodnota prvního parametru je", param1)
    print("Hodnota druhého parametru je", param2)

Všimněte si, že při spuštění příkazu def nedošlo ke spuštění příkazů v těle dané funkce. Při definici funkce dojde pouze k vytvoření objektu, který reprezentuje danou funkci a obsahuje kód definovaný v těle funkce. Odkaz na tento objekt se uloží do proměnné s názvem dané funkce.

In [9]:
# Volání funkce:
print_demo()
Byla spuštěna funkce 'print_demo'.
In [70]:
# Volání funkce s jedním paraetrem, "ahoj" je argument pro parametr 'param'
print_demo1("ahoj")
print_demo1(12)
Hodnota parametru je ahoj
Hodnota parametru je 12
In [13]:
# ukázka: debugger, pytutor
x = print_demo1("ahoj")
print(x)
Hodnota parametru je ahoj
None

Výpis samotného objektu reprezentujícího funkci vypadá takto:

In [15]:
# výpis objektu, který reprezentuje funkci:
print(print_demo)
<function print_demo at 0x7e7b583118a0>
In [13]:
# funkce vrací None:
if print_demo:
    print("ano")
ano

Pokud chceme funkci použít, musíme ji zavolat pomocí kulatých závorek, přičemž musíme předat správný počet argumentů odpovídající definici funkce, jinak dojde k chybě:

In [12]:
# ukázka: funkci musím volat se správným počtem argumentů
print_demo2(5+5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[12], line 2
      1 # ukázka: volání funkce, správný počet argumentů
----> 2 print_demo2(5+5)

TypeError: print_demo2() missing 1 required positional argument: 'param2'
In [16]:
print_demo()        # zde nepředáváme žádný argument
print_demo1(10)     # číslo 10 je argument pro parametr 'param'
print_demo2(10, 20) # čísla 10 je argument pro parametr 'param1', 20 je argument pro parametr 'param2'
Byla spuštěna funkce 'print_demo'.
Hodnota parametru je 10
Hodnota prvního parametru je 10
Hodnota druhého parametru je 20

Syntaxe příkazu def má mnoho různých variant, které souvisí zejména s definicí seznamu parametrů funkce. Zatím se jimi nebudeme zabývat a postupně se k tomuto tématu budeme vracet.

Příklad¶

Napište funkci say_hello(name), která vypíše pozdrav pro uživatele se zadaným jménem. (Anglicky, abychom nemuseli skloňovat...). Funkci několikrát zavolejte, vyzkoušejte různé hodnoty argumentu.

In [22]:
def say_hello(name):
    print("Hello", name)
say_hello("Anna")
say_hello("Petr")
say_hello(123)
Hello Anna
Hello Petr
Hello 123

Příkaz return – vrácení výsledku funkce¶

V matematice se pojmem funkce označuje předpis, který ze vstupní hodnoty (případně vstupních hodnot) počítá nějakou výstupní hodnotu. Předchozí příklady funkcí print_demo atd. nemají žádný výsledek. K vrácení výsledné hodnoty zevnitř funkce na místo, kde byla funkce zavolána, slouží příkaz return. Volání funkce je výraz, může tedy vystupovat např. napravo od přiřazovacího operátoru:

In [17]:
# K vrácení výsledné (návratové) hodnoty funkce slouží příkaz return:
def polynom(x):
    return x ** 2 - 3 * x + 1
    
def polynom1(x):
    vysledek = x ** 2 - 3 * x + 1
    return vysledek

# výslednou (návratovou) hodnotu funkce mohu např. vložit do proměnné:
vysledek = polynom(0.5)
print(vysledek)

# nebo rovnou vypsat:
print(polynom1(2.5))

# volání funkcí může být součástí složitějších výrazů
vysledek = polynom(2.5-4) + 3 - polynom(2.5)  ** 2
print(vysledek)

# volání funkce v cyklu:
for i in range(-10,10):
    print(polynom(i), end = " ")
-0.25
-0.25
10.6875
131 109 89 71 55 41 29 19 11 5 1 -1 -1 1 5 11 19 29 41 55 
In [18]:
# pokud funkce neobsahuje příkaz return, je její návratovou hodnotou None:
def say_hello(name):
    print("Hello", name)

x = say_hello("anna")
print(x)
Hello anna
None
In [20]:
# tato funkce vrací hodnotu 6:
def say_hello(name):
    print("Hello", name)
    return 6

x = say_hello("anna")
print(x)
Hello anna
6
In [21]:
# příkaz return okamžitě ukončí funkci (jako break u cyklu)
# za příkazem return může a nemusí následovat návratová hodnota 
# pokud žádnou návratovou hodnotu nespecifikujeme, vrátí se None
def say_hello(name):
    return
    print("Hello", name)


x = say_hello("anna")
print(x)
None

Příkazem return končí vykonávání dané funkce a žádné další příkazy z těla funkce se nevykonají. To je důležité zejména pokud funkce obsahuje příkazy return uvnitř podmínek nebo cyklů. Pokud funkce neobsahuje žádný příkaz return nebo pokud dojde k vykonání prázdného příkazu return (tj. ukončení funkce bez vrácení hodnoty), je výsledkem funkce hodnota None:

In [34]:
def polynom(x):
    if x < 0:
        print("Definiční obor této funkce nezahrnuje záporná čísla.")
        return
    return x ** 2 - 3 * x + 1

vysledek = polynom(-3)
print(vysledek)
Definiční obor této funkce nezahrnuje záporná čísla.
None
In [35]:
# ukázka signum: další příklad použití příkazu return v různých větvích výpočtu:
def signum(x):
    if x > 0:
        return 1
    elif x == 0:
        return 0
    else:
        return -1

for i in range(-10, 10):
    print(signum(i), end = " ")
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 1 1 1 1 1 1 1 1 1 

Příklad¶

  1. Napište funkci je_sudé(n), která vrací True nebo False podle toho, jestli je zadané přirozené číslo n sudé nebo není. Funkci zavolejte.
In [37]:
def je_sude(n):
    if n % 2 == 0:
        return True
    else:
        return False

print(je_sude(10), je_sude(11))
True False
In [39]:
def je_sude(n):
    return  n % 2 == 0

print(je_sude(10), je_sude(11))
True False
In [40]:
def je_sude(n):
    return not  n % 2 

print(je_sude(10), je_sude(11))
True False
  1. Analogicky napište funkci je_liché(n). Využijte při tom předchozí funkci. Funkci zavolejte.
In [41]:
def je_liche(n):
    if not je_sude(n):
        return True
    else:
        return False
print(je_liche(10), je_liche(11))
False True
In [44]:
def je_liche(n):
    return je_sude(n) == False
print(je_liche(10), je_liche(11))
False True
In [42]:
def je_liche(n):
    return not je_sude(n)
print(je_liche(10), je_liche(11))
False True

Proměnné a obor platnosti¶

Lokální proměnná je proměnná, která je definovaná uvnitř funkce. Jakmile skončí vykonávání dané funkce, zaniknou i všechny lokální proměnné definované uvnitř dané funkce.

In [36]:
# Proměnné dělíme na globální (definované na úrovni programu) 
# a lokální (definované na úrovni funkce)

# globální proměnná
a = 10

def f(x):            # parametr funkce ... lokální proměnná
    y = x + 1        # lokální proměnná    
    print("lokalni", x, y)

f(2 + 3)
print("globalni", a)
lokalni 5 6
globalni 10
In [37]:
# lokální proměnná žije jen uvvnitř funkce, když funkce skončí, zanikne:
a = 10

def f(x):
    y = x + 1
    print("lokalni", x, y)

f(2 + 3)
print("globalni", a)
print("lokalni", y)
lokalni 5 6
globalni 10
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[37], line 10
      8 f(2+3)
      9 print("globalni", a)
---> 10 print("lokalni", y)

NameError: name 'y' is not defined
In [41]:
# lokální proměnnou mohu pojmenovat stejně jako globální proměnnou
# uvnitř funkce lokální proměnná překryje globální proměnnou stejného jména
a = 10

def f(x):
    y = x + 1
    a = 16
    print("lokalni", x, y)
    print("lokalni", a)

f(2 + 3)
print("globalni", a) 
lokalni 5 6
lokalni 16
globalni 10
In [34]:
# globální proměnnou mohu použít uvnitř funkce:
a = 10

def f(x):
    y = x + 1 + a
    print("lokalni", x, y)
    print("globalni", a)

f(5+3)
print("globalni", a)
lokalni 8 19
globalni 10
globalni 10
In [43]:
#  uvnitř funkce mohu použít globální import:
a = 10
import math

def f(x, a):
    y = x + 1 + a + math.cos(45) 
    print("lokalni", x, y)
    print("globalni", a)

f(5+3, a)
print("globalni", a)
lokalni 8 19.52532198881773
globalni 10
globalni 10
In [45]:
#  uvnitř funkce mohu provést lokální import:
a = 10
pi = "ahoj"

def f(x, a):
    from math import pi
    y = x + 1 + a + pi
    print("lokalni", x, y, pi)
    print("globalni", a)

f(5+3, a)
print("globalni", a, pi)
lokalni 8 22.141592653589793 3.141592653589793
globalni 10
globalni 10 ahoj
In [42]:
# globální proměnnou nemohu uvnitř funkce měnit:
a = 10

def f(x):
    y = x + 1 + a
    a += 1
    print("lokalni", x, y)
    print("globalni", a)

f(5+3)
print("globalni", a)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Cell In[42], line 10
      7     print("lokalni", x, y)
      8     print("globalni", a)
---> 10 f(5+3)
     11 print("globalni", a)

Cell In[42], line 5, in f(x)
      4 def f(x):
----> 5     y = x + 1 + a
      6     a += 1
      7     print("lokalni", x, y)

UnboundLocalError: cannot access local variable 'a' where it is not associated with a value
In [42]:
# jakmile uvnitř funkce použiju globální proměnnou, již nesmím definovat
# lokální proměnnou stejného jména:
a = 10

def f(x):
    y = x + 1 + a
    a += 1
    print("lokalni", x, y)
    print("globalni", a)

f(5+3)
print("globalni", a)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Cell In[42], line 10
      7     print("lokalni", x, y)
      8     print("globalni", a)
---> 10 f(5+3)
     11 print("globalni", a)

Cell In[42], line 5, in f(x)
      4 def f(x):
----> 5     y = x + 1 + a
      6     a += 1
      7     print("lokalni", x, y)

UnboundLocalError: cannot access local variable 'a' where it is not associated with a value
In [3]:
# ukázka funkce s jedním parametrem a lokální proměnnou, pytutor
a = 10
x = "ahoj"
y = "neco"
from math import cos

def f(x):
    y = x + 1
    cos = 67
    print("lokalni", x, y, cos)

f(5+3)
print("globalni", a, x, y, cos(1))
lokalni 8 9 67
globalni 10 ahoj neco 0.5403023058681398

Uvnitř funkce máme naopak přístup objektům s širším oborem platnosti, jako jsou např. globální proměnné, ale i globálně importované moduly, jiné funkce definované na stejné úrovni, apod. Názvy definované na vyšší úrovni ale můžeme pouze použít, nemůžeme je změnit – viz následující příklad:

In [10]:
# globální proměnná
globalni = 10

def f():
    # lokální proměnná
    lokalni = 1

    # použití globální proměnné
    lokalni += globalni

    # uvnitř funkce nelze měnit globální proměnné
    #globalni += 1

    # výpis lokálních proměnných
    print("lokalni", lokalni)

# použití funkce
f()

# výpis globálních proměnných
print("globalni", globalni)

# lokální proměnné definované uvnitř funkce nejsou k dispozici na globální úrovni
#print(lokalni)
lokalni 11
globalni 10

Tip: Kliknutím na tlačítko pytutor v rozhraní JupyterLab se zobrazí grafická interpretace toho, co se v programu děje.

Pokud bychom uvnitř funkce použili přiřazení jako např. globalni = 1 v následujícím příkladu, není to chyba, ale je důležité si uvědomit, co se v programu děje. Příkaz přiřazení totiž vytváří novou proměnnou, ke které připojí daný název. V tomto případě tedy vzniká nová lokální proměnná s názvem globalni a hodnotou 1 a její název na lokální úrovni překryje globální proměnnou se stejným názvem, jde však o dva různé objekty:

In [11]:
# globální proměnná
globalni = 10

def f():
    # zde dojde k vytvoření lokální proměnné, která překryje proměnnou na globální úrovni
    globalni = 1

    print("hodnota uvnitř funkce:", globalni)

# použití funkce
f()

# funkce f nezměnila globální proměnnou
print("hodnota mimo funkci:", globalni)
hodnota uvnitř funkce: 1
hodnota mimo funkci: 10

K podobné shodě názvu proměnných může docházet poměrně často, ale není třeba se tohoto chování bát. Vykonávání příkazů dané funkce probíhá ve svém vlastním lokálním světě, který s globálním světem komunikuje především pomocí vstupních parametrů a výstupní návratové hodnoty (viz dále). Spíš by bylo nepříjemné, kdyby funkce modifikovala okolní stav nějakým jiným, nepřehledným způsobem.

Příklady¶

  1. Napište funkci je_dělitelné(n, d), která vrací True nebo False podle toho, jestli je zadané přirozené číslo n dělitelné číslem d.
In [55]:
def je_dělitelné(n, d):
    return n % d == 0
In [56]:
n = 10
for i in range(1, n + 1):
    print(n, i, ":",  je_dělitelné(n, i))
10 1 : True
10 2 : True
10 3 : False
10 4 : False
10 5 : True
10 6 : False
10 7 : False
10 8 : False
10 9 : False
10 10 : True
  1. Napište funkci počet_dělitelů(n), která spočítá a vrátí počet dělitelů přirozeného čísla n. Využijte funkci naprogramovanou v předchozím příkladu.
In [51]:
def počet_dělitelů(n):
    if n <= 0:
        return
    počet = 0
    for i in range(1,n+1):
        if je_dělitelné(n,i):
            počet += 1
    return počet    
In [57]:
for i in range(10):
    print(i, počet_dělitelů(i))
0 None
1 1
2 2
3 2
4 3
5 2
6 4
7 2
8 4
9 3
  1. Napište funkci vypiš_dělitele(n), která vypíše všechny dělitele přirozeného čísla n.
In [62]:
def vypiš_dělitele(n):
    for i in range(1,n+1):
        if je_dělitelné(n,i):
            print(i, end = " ")
In [59]:
for i in range(10):
    print(i, ": ", end="")
    vypiš_dělitele(i)
    print("")
0 : 
1 : 1 
2 : 1 2 
3 : 1 3 
4 : 1 2 4 
5 : 1 5 
6 : 1 2 3 6 
7 : 1 7 
8 : 1 2 4 8 
9 : 1 3 9 
  1. Napište funkci bmi(hmotnost, výška), která ze zadaných parametrů spočítá index BMI a vrátí výsledek jako číselnou hodnotu.
In [65]:
def bmi(hmotnost, výška):
    return hmotnost / (výška**2)
In [67]:
hmotnost = float(input("Zadej svou hmotnost v kg:"))
výška = float(input("Zadej svou výšku v m:"))

bmi_výsledek = bmi(hmotnost, výška)
print("Vaše bmi je", bmi_výsledek)
print(f"Vaše bmi je {bmi_výsledek:.1f}.")
Vaše bmi je 22.03856749311295
Vaše bmi je 22.0.
  1. Naprogramujte funkci součet_číslic(n), která spočítá součet číslic v desítkovém zápisu přirozeného čísla n.
In [68]:
def součet_číslic(n):
    součet = 0
    while n > 0:
        součet += n % 10
        n //= 10 
    return součet

print(součet_číslic(456))
15
  1. Naprogramujte funkci remove(n, digit), která z desítkového zápisu zadaného přirozeného čísla n odstraní všechny číslice, které se rovnají hodnotě digit. Např.:

    • remove(123, 2) → 13
    • remove(243132, 3) → 2412
In [69]:
def remove(n, digit):
    výsledek = 0                # Výsledek
    násobek = 1          
    
    while n > 0:
        číslice = n % 10  # Získej poslední číslici
        n //= 10          # Odstraň poslední číslici
        
        # Pokud číslice není digit, přidej ji k výsledku
        if číslice != digit:
            výsledek += číslice * násobek
            násobek *= 10  # Posuň násobek na další pozici
        
    return výsledek

print(remove(321353,3))
print(remove(444444,4))
215
0

Pro procvičení můžete dále zkusit upravit libovolný příklad z předchozích cvičení a definovat v něm vhodnou funkci.