Topic notes

Modular programs

~11 min read10 runnable examples

These notes are written in Slovak. The examples run the same in any language.

You can run every example right here, then edit the code and see what changes. Your code runs only in this browser, in an isolated environment with no access to the internet or your account.

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.

Done reading?

Check what you remember with a few short tasks on the topic.