Poznámky k téme

Modulárny program

~11 min čítania10 príkladov na spustenie

Každý príklad si môžeš spustiť priamo tu – a potom upraviť kód a skúsiť, čo sa zmení. Tvoj kód beží iba v tomto prehliadači, v izolovanom prostredí bez prístupu k internetu či k tvojmu účtu.

Väčší program sa nepíše ako jeden dlhý blok príkazov. Rozdeľuje sa na menšie časti – funkcie a moduly –, z ktorých každá rieši jednu úlohu. Python navyše obsahuje veľa hotových modulov (štandardnú knižnicu): na matematiku, náhodné čísla, prácu s časom a mnoho ďalšieho. V tejto téme sa naučíš moduly používať, vytvoriť si vlastný modul a rozložiť program na prehľadné časti.

Čo sa naučíš

  • importovať modul a používať jeho funkcie (import, from … import),
  • pracovať s modulmi math, random a time,
  • vytvoriť vlastný modul a rozumieť zápisu if __name__ == "__main__":,
  • rozložiť program na funkcie s jasnými úlohami.

1Import modulu

Modul je súbor s funkciami a hodnotami, ktoré môžeme použiť v našom programe. Najprv ho musíme importovať – zvyčajne na začiatku programu. K jeho obsahu sa potom dostaneme cez meno modulu a bodku:

Python
import math
​
print(math.sqrt(16))
print(math.pi)
print(math.floor(3.7), math.ceil(3.2))

Výstup:

4.0
3.141592653589793
3 4

1.1Spôsoby importu

ZápisPoužitie v programe
import mathmath.sqrt(2), math.pi
from math import sqrt, pisqrt(2), pi – bez mena modulu
import math as mm.sqrt(2) – modul pod kratším menom
from math import *všetko bez mena modulu – neodporúča sa
Python
from math import sqrt, pi
import random as r
​
print(round(sqrt(2), 4), round(pi, 4))
r.seed(1)
print(r.randint(1, 6))

Výstup:

1.4142 3.1416
2

Prvý spôsob (import math) je najprehľadnejší: pri každom použití vidno, odkiaľ funkcia pochádza. Zápis from math import * prinesie do programu desiatky mien naraz a môže nechtiac prepísať tvoje vlastné premenné.

2Modul math

Funkcia / hodnotaVýznamPríkladVýsledok
math.sqrt(x)druhá odmocninamath.sqrt(25)5.0
math.pi, math.ekonštanty π a emath.pi3.141592653589793
math.floor(x)zaokrúhlenie nadolmath.floor(-2.5)-3
math.ceil(x)zaokrúhlenie nahormath.ceil(2.1)3
math.gcd(a, b)najväčší spoločný deliteľmath.gcd(48, 18)6
math.factorial(n)faktoriálmath.factorial(5)120
math.sin(x), math.cos(x)goniometrické funkcie (uhol v radiánoch)math.sin(math.pi / 2)1.0
math.radians(st)stupne na radiánymath.radians(180)3.141592653589793
math.hypot(a, b)prepona: √(a² + b²)math.hypot(3, 4)5.0
>>> import math
>>> math.sqrt(25)
5.0
>>> math.floor(-2.5)
-3
>>> math.factorial(5)
120
>>> round(math.sin(math.radians(30)), 4)
0.5
>>> math.hypot(3, 4)
5.0

Goniometrické funkcie pracujú s uhlami v radiánoch: uhol v stupňoch najprv preveď funkciou math.radians.

3Modul random

Modul random vytvára (pseudo)náhodné čísla – hodí sa na hry, simulácie a generovanie testovacích údajov.

FunkciaČo vráti
random.randint(a, b)náhodné celé číslo od a po b vrátane
random.random()náhodné desatinné číslo od 0 (vrátane) po 1 (bez 1)
random.uniform(a, b)náhodné desatinné číslo medzi a a b
random.choice(z)náhodný prvok zoznamu (alebo znak reťazca)
random.shuffle(z)náhodne premieša zoznam (mení ho)
random.sample(z, k)k rôznych náhodných prvkov
random.seed(n)nastaví „semienko“ – potom sú „náhodné“ čísla zakaždým rovnaké
Python
import random
​
random.seed(42)
print(random.randint(1, 6), random.randint(1, 6), random.randint(1, 6))
print(round(random.random(), 3))
print(random.choice(["kameň", "papier", "nožnice"]))
karty = ["A", "K", "Q", "J", "10"]
random.shuffle(karty)
print(karty)
print(sorted(random.sample(range(1, 50), 6)))

Výstup:

6 1 1
0.742
kameň
['J', 'A', 'Q', '10', 'K']
[6, 28, 35, 38, 44, 48]

Počítač náhodu iba napodobňuje: čísla vznikajú výpočtom zo „semienka“ (seed). Bez random.seed sa semienko nastaví podľa času, takže každé spustenie dá iné čísla. S pevným semienkom dostaneš pri každom spustení rovnakú postupnosť – to sa hodí pri hľadaní chýb a testovaní (preto ho používame aj v ukážkach v týchto poznámkach).

4Modul time

Python
import time
​
zaciatok = time.time()
sucet = 0
for i in range(1_000_000):
sucet += i
trvanie = time.time() - zaciatok
print("Súčet:", sucet)
print("Výpočet trval menej ako sekundu:", trvanie < 1)

Výstup:

Súčet: 499999500000
Výpočet trval menej ako sekundu: True

time.time() vráti počet sekúnd od roku 1970 – rozdiel dvoch takýchto časov je trvanie výpočtu. time.sleep(s) zastaví program na s sekúnd (hodí sa napríklad pri jednoduchých animáciách v textovom režime).

5Vlastný modul

Vlastný modul je obyčajný súbor .py s funkciami. Uložíme ho do toho istého priečinka ako program, ktorý ho používa:

Súbor geometria.py:

"""Výpočty pre rovinné útvary."""
PI = 3.141592653589793
​
def obsah_kruhu(r):
return PI * r ** 2
​
def obvod_kruhu(r):
return 2 * PI * r
​
def obsah_obdlznika(a, b):
return a * b

Program ho importuje podľa mena súboru (bez .py):

Python
import geometria
​
print(round(geometria.obsah_kruhu(2), 2))
print(round(geometria.obvod_kruhu(2), 2))
print(geometria.obsah_obdlznika(3, 4))

Výstup:

12.57
12.57
12

5.1if __name__ == "__main__"

Pri importe Python vykoná celý súbor modulu. Ak by sme v module mali aj skúšobné príkazy (print(obsah_kruhu(1))), vypísali by sa pri každom importe. Riešením je podmienka:

Súbor kocka.py:

import random
​
def hod():
return random.randint(1, 6)
​
if __name__ == "__main__":
# vykoná sa iba pri priamom spustení súboru kocka.py
print("Skúšobný hod:", hod())

Premenná __name__ má hodnotu "__main__" iba vtedy, keď súbor spúšťame priamo. Pri importe obsahuje meno modulu ("kocka"), takže sa skúšobná časť nevykoná:

Python
import kocka
​
print("Hody:", [kocka.hod() for _ in range(5)])

Výstup:

Hody: [1, 1, 1, 3, 2]

Rovnaký zápis používajú aj úlohy na Mente, ktoré testujú tvoje funkcie: hlavný program (načítanie vstupu a výpis) patrí pod if __name__ == "__main__":, aby ho testy pri volaní funkcií nespúšťali.

6Rozklad programu na funkcie

Pri väčšej úlohe sa oplatí postupovať zhora nadol: najprv rozdeliť úlohu na menšie podúlohy, každú podúlohu zapísať ako funkciu a hlavný program poskladať z volaní týchto funkcií. Výhody:

  • každá funkcia je krátka a dá sa samostatne vyskúšať,
  • hlavný program sa číta ako obsah: čo sa robí, v akom poradí,
  • chyba sa hľadá v malom kúsku kódu, nie v stovkách riadkov,
  • funkciu možno použiť opakovane (aj v inom programe – cez modul).

Príklad: vyhodnotenie testu. Podúlohy: načítať body, vypočítať percentá a známku, vypísať správu.

Python
MAXIMUM = 50
​
def nacitaj():
meno = input("Meno: ")
body = int(input("Body: "))
return meno, body
​
def percenta(body):
return body / MAXIMUM * 100
​
def znamka(p):
hranice = [(90, 1), (75, 2), (50, 3), (30, 4)]
for hranica, z in hranice:
if p >= hranica:
return z
return 5
​
def vypis(meno, p, z):
print(f"{meno}: {p:.1f} % – známka {z}")
​
def main():
meno, body = nacitaj()
p = percenta(body)
vypis(meno, p, znamka(p))
​
if __name__ == "__main__":
main()

Výstup:

Meno: Ema
Body: 43
Ema: 86.0 % – známka 2

Hlavný program je zabalený do funkcie main – vďaka tomu nemá globálne premenné a celý „príbeh“ programu je na jednom mieste. Funkcie percenta a znamka iba počítajú (nič nevypisujú ani nečítajú), takže sa dajú použiť aj inde.

7Riešené úlohy

7.1Hod mincou

Zadanie: Simuluj 1000 hodov mincou a vypíš, koľkokrát padol znak a koľkokrát hlava, aj v percentách.

Python
import random
​
znak = 0
for _ in range(1000):
if random.choice(["znak", "hlava"]) == "znak":
znak += 1
hlava = 1000 - znak
print(f"znak: {znak} ({znak / 10:.1f} %)")
print(f"hlava: {hlava} ({hlava / 10:.1f} %)")

Výstup:

znak: 512 (51.2 %)
hlava: 488 (48.8 %)

Pri veľa hodoch sa podiel blíži k 50 %, ale takmer nikdy nevyjde presne.

7.2Histogram hodov kockou

Zadanie: Hoď 60-krát kockou a pre každú stranu vypíš riadok s toľkými hviezdičkami, koľkokrát padla.

Python
import random
​
pocty = [0] * 7 # indexy 1 až 6 (index 0 nepoužijeme)
for _ in range(60):
pocty[random.randint(1, 6)] += 1
for strana in range(1, 7):
print(strana, "*" * pocty[strana], pocty[strana])

Výstup:

1 ********** 10
2 *********** 11
3 **************** 16
4 ******** 8
5 ********* 9
6 ****** 6

7.3Generátor hesiel

Zadanie: Vygeneruj náhodné heslo dĺžky 10 z malých a veľkých písmen a číslic.

Python
import random
import string
​
znaky = string.ascii_letters + string.digits
heslo = "".join(random.choice(znaky) for _ in range(10))
print(heslo)

Výstup:

ox9yimTcfi

Modul string obsahuje hotové reťazce znakov: string.ascii_lowercase (a–z), string.ascii_uppercase (A–Z), string.digits (0–9). Na skutočné heslá sa používa modul secrets, ktorého náhoda sa nedá predpovedať.

8Časté chyby

  • Chýbajúci import: NameError: name 'math' is not defined – na začiatok patrí import math.
  • Chýbajúce meno modulu: po import math treba písať math.sqrt, nie sqrt (alebo použiť from math import sqrt).
  • Vlastný súbor s menom štandardného modulu (random.py) – premenuj ho.
  • Modul v inom priečinku – vlastný modul musí byť v priečinku programu.
  • Celý program v jednom bloku – ťažko sa číta aj opravuje; rozdeľ ho na funkcie.

Zhrnutie

  • import modul a potom modul.meno; from modul import meno (bez predpony); import modul as m (alias).
  • math: sqrt, pi, floor, ceil, gcd, factorial, goniometria v radiánoch.
  • random: randint (vrátane hraníc), random, choice, shuffle, sample, seed.
  • Vlastný modul = súbor .py v priečinku programu; import podľa mena súboru.
  • if __name__ == "__main__": – kód, ktorý sa vykoná iba pri priamom spustení, nie pri importe.
  • Rozklad na funkcie: jedna funkcia = jedna podúloha; hlavný program (main) iba volá funkcie.

Úlohy na precvičenie

  1. Vypočítaj dĺžku prepony pravouhlého trojuholníka s odvesnami 5 a 12 dvoma spôsobmi (math.sqrt a math.hypot).
  2. Simuluj 10 000 hodov dvoma kockami a zisti, ktorý súčet padá najčastejšie.
  3. Vytvor modul texty.py s funkciami pocet_samohlasok(s) a je_palindrom(s) a použi ich v inom programe.
  4. Napíš hru Kameň, papier, nožnice proti počítaču na 5 kôl, rozloženú na funkcie (voľba počítača, vyhodnotenie kola, výpis skóre).
  5. Zmeraj, ako dlho trvá zoradenie zoznamu milióna náhodných čísel funkciou sorted.

Prečítané?

Over si, čo si z poznámok pamätáš, na krátkych úlohách k téme.