
Python / 6. časť
Súbory
V čase, keď pracujeme so spusteným programom (runtime), sú všetky naše údaje uložené v pamäti počítača (RAM), ktorej obsah je po ukončení vykonávania programu, resp. pri vypnutí počítača, zmazaný. Ak chceme spracúvané údaje uchovať na budúce využite, musíme ich uložiť do súborov (files). O správu súborov sa stará súborový podsystém operačného systému. Ten súbory organizuje v adresárovej (directory) štruktúre, pričom na adresovanie konkrétnej zložky alebo súboru používa cesty (paths).
Objekt, s ktorým pracujeme pri manipulácii so súbormi, nazývame tzv. držadlom (handle). Pri vytváraní handle určujeme, či chceme súbor otvoriť na čítanie ‘r‘, zápis ‘w‘ alebo doplnenie ‘a‘. Obsah súboru môžeme čítať/zapisovať buď riadok po riadku, alebo ako jeden celok. Otvorenie neexistujúceho súboru na čítanie vedie k chybe. Pri zápise do súboru, ktorý neexistuje, sa vytvorí nový súbor. Existujúci súbor sa prepíše (na zápis musíme mať, samozrejme, príslušné práva).
# Zápis písmen A B C do súboru test.txt (mfile je handle)
with open("test.txt","w") as mfile:
mfile.write("A\n")
mfile.write("B\n")
mfile.write("C\n")
# Čítanie obsahu súboru test.txt riadok po riadku
with open("test.txt","r") as mfile:
for riadok in mfile:
print(riadok,end = "")
Optimalizácia kódu
Pri programovaní v jazyku Python sa odporúča kód optimalizovať nasledujúcimi spôsobmi:
- nepoužívame príliš dlhé riadky,
- definície funkcií uvádzame na začiatku modulov pod časť import, na oddelenie definícií používame prázdne riadky,
- názvy funkcií píšeme malými písmenami s podčiarkovníkom, v tele funkcií uvádzame dokumentačné reťazce,
- používame tzv. čisté funkcie (pure functions) namiesto modifikátorov – modifikátory menia obsah odovzdaných argumentov, čím vzniká nežiaduci bočný efekt (side effect),
- využívame priamy výpis výsledku Boolean výrazov, resp. tieto výrazy vraciame priamo výrazom return,
- používame nekonečné cykly,
- nepoužívame výraz ‘== True‘,
- ak nepotrebujeme uchovávať údaje, neuchovávajme ich.
Množiny a pevné množiny
Jedna z podstatných charakteristík typov tuple a list je taká, že položky ich záznamov sú zoradené, teda na ich výber môžeme použiť indexovací operátor. Ďalšia dôležitá vlastnosť typu tuple je nemeniteľnosť jeho položiek. Naopak, položky typu list meniť možno. Uvedené charakteristiky môžeme zhrnúť v jednoduchej tabuľke:
|
položky zoradené = indexovateľné |
nemeniteľné |
tuple |
meniteľné |
list |
Jediný nezoradený typ s meniteľnými položkami, ktorému sme sa doposiaľ venovali, je typ dict. Ten však nie je sekvenčným typom, ale mapovacím. Jeho obsahom teda nie je zoznam položiek, ale zoznam párov. Python preto zavádza ďalšie dva dátové typy, ktoré dopĺňajú uvedené typy o množiny s nezoradenými položkami:
|
položky zoradené |
položky nezoradené = neindexovateľné |
nemeniteľné |
tuple |
frozenset |
meniteľné |
list |
set |
Množiny (sets) a pevné množiny (frozensets) disponujú vlastnosťami a umožňujú vykonávať operácie podobné slovníkom. Definícia množiny/pevnej množiny má nasledujúci tvar:
mnozina = set([“a“,“b“,“c“,…])
mnozina = frozenset([“a“,“b“,“c“,...])
Na prácu s množinami môžeme využívať napr. nasledujúce metódy:
add() |
Pridanie položky do množiny, ak ešte v množine neexistuje |
union(), | |
Spojenie množín |
intersect(), & |
Prienik množín = položky, ktoré sú v množinách rovnaké |
difference(), - |
Položky, ktoré sú v množinách rozdielne |
clear() |
Vymazanie obsahu množiny |
Rekurzia
Jedna z najbežnejších funkcionalít, ktorá je implementovaná v mnohých programovacích jazykoch, je tzv. rekurzia. Ide o programovú konštrukciu, v rámci ktorej funkcia volá samu seba. Väčšina rekurzií sa dá riešiť aplikáciou cyklu for, no v prípadoch, pre ktoré je rekurzia príznačná, je vhodnejšie použiť práve túto konštrukciu. Na správne fungovanie musí rekurzia v určitom kroku dospieť k bodu ukončenia (návratu). V opačnom prípade dôjde k nekonečnému zacykleniu a skôr či neskôr k prekročeniu pamäťového limitu (maximum recursion depth).
K najznámejším aplikáciám rekurzie určite patria tzv. fraktály. Tie sú najčastejšie zobrazované v podobe grafických obrazcov, pričom komplexný celok (high-level) sa skladá z teoreticky nekonečného množstva rovnako vyzerajúcich menších celkov (low-level). Príklad fraktálu stromu možno vidieť na obrázku. V tele funkcie draw_fracttree() možno nájsť dve jej vlastné volania. Prvé z nich (posun doľava) sa vykoná určený počet krát, pričom po dosiahnutí (vykonaní) posledného kroku program pokračuje na druhom volaní (posun doprava). Takto sa postupuje smerom vpred a vzad, až pokiaľ nebudú vykonané všetky kroky rekurzie v smere doľava aj doprava.
Aplikáciu rekurzie možno vidieť na príklade č. 8, pomocou ktorého riešime kreslenie fraktálového stromu:
import matplotlib.pyplot as plt
import math
fig,ax = plt.subplots()
ax.set(xlim = [0,600],ylim = [0,550],
title = "Fraktálový strom")
# Rekurzívne kreslenie stromu
def draw_fracttree(x1,y1,uhol,krok):
# Podmienka ukončenia rekurzie
if krok:
x2 = x1+int(math.cos(math.radians(uhol))*krok*8.0)
y2 = y1+int(math.sin(math.radians(uhol))*krok*10.0)
# Čiara z bodu x1,y1 do bodu x2,y2
ax.plot([x1,x2],[y1,y2])
# Rekurzívne volanie tej istej funkcie
draw_fracttree(x2,y2,uhol-20,krok-1)
draw_fracttree(x2,y2,uhol+20,krok-1)
# Prvotné volanie rekurzívnej funkcie
# - štart kreslenia stromu v bode 300,0
# - uhol natočenia čiary v 1. kroku 90 stupňov
# - počet krokov rekurzie 10
draw_fracttree(300,0,90,10)
plt.show()
V prípade rekurzívnych funkcií môžeme využiť možnosť vzájomného rekurzívneho volania (mutual recursion) viacerých funkcií, pri ktorom funkcia A volá funkciu B a funkcia B volá funkciu A. V tomto prípade je kriticky dôležité správne nastaviť bod ukončenia rekuzie tak, aby nedošlo k nekonečnému zacykleniu.
Rekurzívne typy
Už viackrát sme uviedli, že viaceré dátové typy môžu byť vnorené, teda môžu obsahovať položky, ktoré sú rovnakého typu, ako je typ, v ktorom sa nachádzajú. Ak má typ v rámci svojej definície použité položky vlastného typu, hovoríme o tzv. rekurzívnej definícii alebo rekurzívnej dátovej štruktúre. V prípade dátových typov však na rozdiel od klasickej rekurzie nejde o cyklickú rekurziu, ale o rekurziu, ktorá sa po pár krokoch končí. Na príklade možno vidieť definíciu typu list, ktorého v poradí štvrtá položka je takisto typom list. Navyše štvrtá položka tejto vnorenej položky je takisto typom list. Z uvedeného dôvodu nemožno položky premennej cisla zosumarizovať jednoduchým použitím indexu. Aj v tomto prípade si môžeme pomôcť jednoduchou rekurzívnou funkciou, ktorá diferencuje položky záznamu na položky typu int a položky typu list.
Príklad č. 9 demonštruje použitie rekurzívneho typu list:
# Rekurzívny záznam typu list
cisla = ([1,2,3,[1,2,3,[1,2,3],4,5,6],4,5,6])
# Súčet čísel v zázname - rekurzívna funkcia
def recursive_sum(zaznam):
total = 0
if len(zaznam) == 0:
return 0
for polozka in zaznam:
if type(polozka) is list:
total += recursive_sum(polozka)
else:
total += polozka
return total
print("Záznam typu list:",cisla)
print("Súčet čísiel v zázname je:",recursive_sum(cisla))