KMI/ZPP2 - 10. seminář - Organizace zdrojového kódu
Dosud jsme jako základní nástroj pro abstrakci funkce, které nám sloužili k rozdělení kódu do jednotlivých částí vykonávající nějaký výpočet. Pokud bychom ale měli k dispozici jen funkce, tak by při větších projektech měl soubor se zdrojovým kódem několik desítek tisíc řádků. V takovém případě by orientace v takovém kódu byla velmi náročná.
Proto nám jazyk Python (a další jazyky) umožňuje rozdělit zdrojový kód do samostatných souborů, kde v jednom souboru máme funkce sloužící například pro práci se seznamy. Tímto docílíme ještě lepšího rozdělení zdrojového kódu. Soubor s funkcemi které slouží k řešení konkrétního problému nazýváme knihovna.
Moduly
Jako modul označujeme v jazyce Python soubor s příponou .py. Jedná se
o běžný soubor se zdrojovým kódem, u kterého předpokládáme, že bude
vložen do jiného souboru. Nepředpokládá se tedy spuštění takového souboru samostatně.
Jako příklad si vytvoříme soubor rmath.py
který bude obsahovat následující funkci:
PRECISION = 0.00000001
# výpočet druhé odmocniny pomocí Newtonovy metody
# https://cs.wikipedia.org/wiki/Metoda_tečen
def my_sqrt(number):
estimate = number
while True:
new_estimate = 0.5 * (estimate + number / estimate)
if abs(new_estimate - estimate) < PRECISION:
return new_estimate
estimate = new_estimate
# výpočet n-té mocniny
def my_power(base, exponent):
return float(base ** exponent)
Vložení modulu
Pro vložení knihovny používáme příkaz import
.
import file
Po vykonání příkazu import
se provede program který,
je zapsaný v souboru a vytvoří se nová proměnná pomocí které přistupujeme
k funkcím a proměnným zapsaných v importovaném souboru.
# importování souboru rmath.py
import rmath
# zavolání funkce my_sqrt
rmath.my_sqrt(2)
# výpis konstanty PRECISION
print(rmath.PRECISION)
Jméno pomocí kterého přistupujeme k funkcím a proměnným se nazývá namespace
(jmenný prostor). Díky tomu víme z jaké knihovny danou funkci (proměnnou) používáme. Příkaz
import
umožňuje přejmenovat namespace.
import file as name
Příklad
import rmath as m
m.my_sqrt(2)
V jazyce Python je možné importovat pouze část importovaného souboru, například pouze jednu funkci nebo proměnnou.
from file import part
Například můžeme chtít importovat pouze funkci my_power
from rmath import my_power
my_power(2, 10)
Importovanou část programu je možné přejmenovat (pomocí as
) a
je možné importovat více částí současně.
# import a přejmenování více částí programu
from rmath import my_sqrt as f1, my_power as f2
f1(2) # volání funkce my_sqrt()
f2(2, 2) # volání funkce my_power()
V případě, že chceme importovat celý obsah souboru, lze použít symbol * (hvězdička).
from rmath import *
Pro úplnost dodejme, že při použití from příkazu dochází k importování pouze specifikovaných částí.
from rmath import my_sqrt
# funkce funguje správně (má přístup ke konstantě PRECISION)
my_sqrt(2) # vypíše: 1.414213562373095
# náš program nemá přístup ke konstantě PRECISION
print(PRECISION) # způsobí chybu: NameError: name ’PRECISION’ is not defined
Pro lepší organizaci zdrojového kódu můžeme soubory umisťovat do adresářů. Při importu, pak vkládáme celou relativní cestu k souboru se zdrojovým kódem. Jako oddělovače adresářů používáme symbol . (tečka).
import my_library.rmath
# použití
my_library.rmath.my_sqrt(2)
# NEBO
import my_library.rmath as m
# použití
m.my_sqrt(2)
Existující knihovny
Jazyk Python obsahuje spoustu předprogramovaných knihoven.
Příkladem může být knihovna math
.
# import knihovny math (součást jazyka Python)
import math
# druhá odmocnina
print(math.sqrt(2))
Všechny dostupné knihovny lze najít na adrese: https://docs.python.org/3/library/.
Úkoly
matice_txt_IO.py
, který zapíše matici do textového souboru a matici z takto vytvořeného souboru opět přečte. Pro práci můžete využít úkoly z minulého semináře.
Příklad použití:
M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] matice_txt_IO.uloz_matici("matice.txt", M) nactena_matice = matice_txt_IO.nacti_matici("matice.txt") print(nactena_matice)Vypíše
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]Obsah souboru matice.txt
1,2,3 4,5,6 7,8,9
tree.py
, který implementuje uspořádaný binární strom. Modul bude poskytovat tyto funkce:
tree_add(tree_root, hodnota) tree_find(tree_root, hodnota)Modul importujte do programu
main_tree.py
, kde ověříte funkčnost.
Obsah main_tree.py
bude mj. obsahovat:
tree_root = [] tree_add(tree_root, 42) tree_add(tree_root, 15) tree_add(tree_root, 16) tree_add(tree_root, 72) tree_add(tree_root, 5) print(tree_find(tree_root, 16)) print(tree_find(tree_root, 5)) print(tree_find(tree_root, 88))Po spuštění program vypíše:
True True FalsePro implementaci můžete využít kódu z 5. semináře.
fronta.py
, ve kterém implementuje tyto funkce:
vytvor_frontu() pridej_do_fronty(fronta, hodnota) odeber_z_fronty(fronta)Modul importujte do programu
main_fronta.py
, kde ověříte funkčnost.
Obsah main_fronta.py
bude následující:
import fronta f1 = fronta.vytvor_frontu() fronta.pridej_do_fronty(f1, "Hello") fronta.pridej_do_fronty(f1, "world") f2 = fronta.vytvor_frontu() fronta.pridej_do_fronty(f2, "fist") fronta.pridej_do_fronty(f2, "second") print(fronta.odeber_z_fronty(f1)) print(fronta.odeber_z_fronty(f1)) print(fronta.odeber_z_fronty(f2)) print(fronta.odeber_z_fronty(f2)) print(fronta.odeber_z_fronty(f1)) print(fronta.odeber_z_fronty(f2))Po spuštění program vypíše:
Hello world fist second None NoneBude možnost vytvářet libovolný počet front. Do front bude možno prvky libovolně přidávat a získávat. Při pokusu o odebrání z prázdné fronty bude vráceno None. Nápověda: Pro uchování odkazu na frontu je možno samotnou frontu "obalit" seznamem.