Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi směřující k druhému vydání.

Programování s využitím modulů

O čem si budeme povídat?

Co to je modul?

Čtvrtý element programování do hry zapojuje použití modulů (viz Společné vlastnosti programů). Ve skutečnosti není naprosto nezbytné a i bez něj, s použitím toho, o čem jsme se zatím bavili, můžete vytvořit docela působivé programy. Jenomže s tím, jak se program stává větším, stává se stále obtížnějším sledování toho, co a kde se v něm děje. Potřebujeme použít způsob, který by nám umožnil potlačit nutnost uvažování v některých detailech tak, abychom mohli uvažovat o řešeném problému a abychom nemuseli řešit všechny detaily, které souvisejí se způsobem fungování počítače. Python, BASIC a další jazyky to do jisté míry řeší již svými zabudovanými schopnostmi — nemusíme se například zabývat detaily počítačového technického vybavení (hardware), nemusíme se zabývat tím, jak se čtou stisky jednotlivých kláves na klávesnici a podobně.

Používání modulů má programátorovi umožnit rozšiřování zabudovaných schopností jazyka. Zabývá se obalováním kousků programů do modulů tak, abychom je mohli využít v našich programech. První formou modulu byl podprogram, což byl blok kódu, na který bylo možno skočit (něčím jako příkaz GOTO, o kterém jsme se zmínili v kapitole o větvení). Jakmile byl blok dokončen, bylo možné skočit zpět do místa odkud byl volán. Tato specifická podoba modularity je známa pod pojmy procedura nebo funkce. V jazyce Python a v některých dalších jazycích se slovo modul spojilo se specifickým významem, o kterém si zachvíli něco povíme. Ale nejdříve se podívejme podrobněji na funkce.

Používání funkcí

Dříve, než se budeme zabývat tím, jak můžeme funkci vytvořit, podívejme se, jak používáme velké množství funkcí, které se k libovolnému programovacímu jazyku dodávají (často se dodávají v podobě takzvané knihovny).

Použití některých funkcí a seznam dalších funkcí jsme již viděli, když jsme se bavili o operátorech. Nyní se zaměříme na to, co mají všechny funkce společné a jak je můžeme používat v našich programech. Základní struktura funkce vypadá následovně:

Promenna = nejakaFunkce(argument, dalsi, atd...)

To znamená, že proměnné Promenna je přiřazena hodnota, která je získána jako výsledek volání funkce. Funkce může mít žádný nebo více argumentů, ke kterým se chová jako k interním proměnným. Funkce mohou uvnitř svého kódu volat další funkce. Vezměme si některé příklady v našich vybraných jazycích a podívejme se, jak to funguje:

BASIC: MID$(str$,n,m)

Tato funkce vrací m znaků z řetězce str$, počínaje pozicí n. (Vzpomeňte si, že když v jazyce BASIC uvedeme za jméno proměnné '$', říkáme tím, že pracujeme s řetězcem.)

den$ = "pondělí úterý středa"
PRINT "Dnes je";MID$(den$,8,6)

Vytiskne se "Dnes je úterý".

BASIC: ENVIRON$(str$)

Tato funkce vrací obsah proměnné prostředí (environment variable), jejíž jméno je uložené v str$.

PRINT ENVIRON$("PATH")

Vytiskne obsah proměnné prostředí PATH, která se například v systému DOS obvykle nastavuje uvnitř souboru autoexec.bat.

Tcl: llength L

Vrací délku seznamu L.

set a {"první" "druhý" "třetí"} # seznam s třemi prvky
puts [llength $a]               # výsledkem bude řetězec '3'

Poznámka: V jazyce Tcl je funkcí téměř vše (Tcl místo pojmu funkce upřednostňuje pojem příkaz). Vede to sice k poněkud podivné syntaxi, ale naopak pro počítač je díky tomu zpracování Tcl programů velmi snadné. To je velmi důležité, protože zkratka Tcl znamená Tool Control Language (doslova jazyk pro řízení nebo ovládání nástrojů). Jazyk Tcl byl navržen tak, aby jej bylo možno zabudovat do jiných programů a aby v nich plnil úlohu makro jazyka, podobně jako se Visual Basic for Applications (VBA) používá v produktech firmy Microsoft. Ve skutečnosti můžete stejným způsobem do něčeho jiného vestavět i Python, ale jazyk Tcl je unikátní v tom, že myšlenka pro jeho zabudování do jiných prostředků byla při jeho návrhu od samého počátku prioritní.

Python: pow(x,y)

x = 2                #  použijeme 2 jako hodnotu základu
for y in range(0,11):
   print pow(x,y)    # umocní 2 na y, tj. na 0 až 10

V tomto příkladu generujeme hodnoty y od 0 do 10 a voláme zabudovanou funkci pow() s dvěma argumenty: x a y. Při každém volání jsou aktuální hodnoty x a y dosazeny do volání funkce a výsledek je vytištěn.

Poznámka: Operátor pro umocňování ** je ekvivalentem funkce pow().

Python: dir(m)

Další užitečnou zabudovanou funkcí jazyka Python je funkce dir(). Když jí předáme jméno modulu, vrátí nám seznam platných jmen — často jsou to jména funkcí —, které modul obsahuje. Vyzkoušejte si ji pro vypsání seznamu jmen zabudovaných funkcí:

print dir(__builtins__)

Poznámka: Pokud budete chtít učinit totéž pro libovolný jiný modul, musíte jej nejdříve zpřístupnit příkazem import. V opačném případě si Python bude stěžovat, že jméno modulu nezná.

Dříve, než se pustíme do dalších věcí, měli bychom si o modulech v jazyce Python říci více podrobností.

Používání modulů

Python je extrémně rozšiřovatelný jazyk (stejně, jako je i Tcl) v tom smyslu, že můžete přidávat nové možnosti tím, že provedete import potřebných modulů. Zakrátko si ukážeme, jak můžeme moduly vytvářet. Ale nejdříve si trochu pohrajeme z některými standardními moduly, se kterými se Python již dodává.

sys

S modulem sys jsme se již potkali v okamžiku, kdy jsme z kódu v jazyce Python volali funkci exit za účelem ukončení programu. Modul obsahuje celou řadu dalších užitečných funkcí. Abychom k nim získali přístup, musíme provést import sys:

import sys # zpřístupní vnitřní funkce
sys.exit() # uvádíme předponu 'sys'

Pokud víme, že budeme funkce modulu používat velmi často a pokud nemají stejná jména jako funkce, které jsme již dříve importovali nebo vytvořili, pak můžeme psát:

from sys import *  # import všech jmen z modulu sys 
exit()             # nyní nemusíme uvádět předponu 'sys'

Další moduly a jejich obsah

Uvedeným způsobem můžeme importovat libovolné pythonovské moduly. Týká se to i modulů, které si sami vytvoříte. Za okamžik si ukážeme, jak na to. Ale nejdříve si uděláme krátkou přehlídku některých standardních modulů jazyka Python a představíme si něco z toho, co nabízejí:

Jméno modulu Popis
sys Umožňuje interakci se systémem Python:
  • exit() — ukončení běhu programu
  • argv — zpřístupňuje argumenty z příkazového řádku
  • path — zpřístupňuje obsah systémové proměnné path
  • ps1 — mění vyzývací řetězec '>>>' příkazového řádku interpretu jazyka Python
os Umožňuje interakci s operačním systémem:
  • open — otevření souboru
  • system — provedení příkazu systému
  • mkdir — vytvoření adresáře
  • getcwd — zjistí současný pracovní adresář (z anglického get current working directory)
string Umožňuje manipulaci s řetězci:
  • atoi/f/l — převádí řetězec na typy integer/float/long
  • find — nalezne podřetězec
  • split — rozdělí řetězec na slova
  • upper/lower — převod velkých písmen malá a naopak
re Umožňuje manipulaci s řetězci předepsanou regulárními výrazy, jaké se používají v systému Unix:
  • search — hledej vzorek kdekoliv v řetězci
  • match — hledej pouze od začátku řetězce
  • split — rozděl na podřetězce, které jsou odděleny zadaným vzorkem
  • sub, subn — náhrada řetězců
math Zpřístupňuje řadu matematických funkcí:
  • sin, cos, atd. — trigonometrické funkce
  • log, log10 — přirozený a dekadický logaritmus
  • ceil, floor — zaokrouhlení na celé číslo nahoru a dolů
  • pi, e — konstanty
time Funkce pro práci s časem a datem:
  • time — vrací současný čas (vyjádřený v sekundách)
  • gmtime — převod času v sekundách na UTC (tj. na čas v univerzálních časových souřadnicích — známější pod zkratkou GMT z anglického Greenwich Mean Time, tedy greenwichský [grinidžský] čas)
  • localtime — převod do lokálního času (tj. posunutého vůči UTC o celé hodiny)
  • mktime — opačná operace k localtime
  • sleep — pozastaví běh programu na zadaný počet sekund

Uvedené funkce představují pouze špičku ledovce. V distribuci systému Python se nacházejí doslova tucty modulů a mnoho dalších si můžete stáhnout z Internetu (jejich dobrým zdrojem jsou stránky Vaults of Parnassus). Nahlédněte do dokumentace a najdete informace o funcích pro přístup na Internet, pro grafiku, pro tvorbu databází, atd.

Je důležité si uvědomit, že u většiny programovacích jazyků jsou tyto funkce buď zabudované nebo jsou součástí jejich standardní knihovny. Než začnete psát nějakou funkci, vždycky si v dokumentaci ověřte, zda již náhodou neexistuje. Tím jsme se pěkně dostali k tématu…

Definice našich vlastních funkcí

Nyní již víme, jak používat existující funkce. Ale jak můžeme vytvořit novou funkci? Jednoduše ji definujeme. To znamená, že napíšeme příkaz, který interpretu říká, že definujeme blok kódu, který má být na požádání vsunut na jiná místa v našem programu. (Myslí se tím vsunutí funkčnosti a nikoliv rozepsání v podobě zdrojového textu.)

Vytvořme funkci, která nám vytiskne tabulku násobků libovolného čísla, které zadáme jako argument. V jazyce BASIC by to vypadalo takto:

SUB NASOBKY (N%)
FOR I = 1 TO 12
    PRINT I; "x"; N%; "="; I * N%
NEXT I
END SUB

Funkci můžeme volat tímto způsobem:

PRINT "Tady je tabulka násobků číslem 7 ..."
NASOBKY(7)

Poznámka: V uvedené funkci jsme definovali parametr označený N% a při jejím volání jsme předali argument o hodnotě 7. Když je funkce zavolána, pak její lokální proměnná N% získá hodnotu 7. U funkce můžeme definovat tolik parametrů, kolik potřebujeme. Při jejím volání ale musí být každému parametru přiřazena hodnota. Některé programovací jazyky vám umožňují definovat přednastavené hodnoty parametrů (default value), takže pokud není jejich hodnota při volání určena, použije se přednastavená hodnota. (V odborné terminologii se používá pojem implicitní hodnota parametru, která se použije, pokud hodnota parametru není explicitně určena.) Později si ukážeme, jak se to dělá v jazyce Python.

V jazyce Python by funkce pro tisk tabulky násobků vypadala takto:

def nasobky(n):
    for i in range(1, 13):
        print "%d x %d = %d" % (i, n, i*n)

A volali bychom ji takto:

print "Tady je tabulka násobků číslem 9 ..."
nasobky(9)

Povšimněte si, že tyto funkce nevracejí žádnou hodnotu. Takovým funkcím se v některých jazycích říká procedury. Povšimněte si, že verze příkladu v jazyce BASIC používá klíčové slovo SUB a ne FUNCTION. Jde o zkratku slova subroutine (v češtině podprogram), což je nyní již málo používaný termín z dob programování v assembleru (tj. v jazyce symbolických instrukcí). V jazyce BASIC se jím označují funkce, které nevracejí hodnotu. Python používá klíčové slovo def, které je zkrácenou formou slova define (definuj). To, co následuje, se považuje za definici funkce.

Vzpomínáte si, že jsem se zmínil o použití přednastavených hodnot? Příklad rozumného použití přednastavené hodnoty parametru si ukážeme na funkci, která vrací jméno dne v týdnu. Pokud ji zavoláme bez zadání argumentu, budeme mít na mysli dnešní den. V ostatních případech bude argumentem pořadové číslo dne v týdnu:

# Hodnota dne -1 znamená 'dnes'.
def denTydne(CisloDne = -1):
    dny = ['pondělí', 'úterý',
           'středa', 'čtvrtek', 
           'pátek', 'sobota', 'neděle']
                
    # Zkontrolujeme, zda nejde o přednastavenou hodnotu.        
    if CisloDne == -1:
        # Použij funkce modulu time k získání současného času
        # -- viz tabulka uvedená výše a viz oficiální dokumentace modulu.
        import time
        cas = time.localtime(time.time())
        CisloDne = cas[6]
    return dny[CisloDne]

Poznámka: Modul time potřebujeme použít jen v případě, kdy se uplatní přednastavená hodnota parametru. Operaci import proto odložíme až na dobu, kdy modul skutečně potřebujeme. V případech, kdy přednastavená hodnota není nikdy použita, pak bude náš program o něco výkonnější.

Nyní můžeme funkci zavolat:

print "Dnes je %s." % denTydne()
# Zapamatujte si, že v počítačové řeči začínáme nulou.
# Nula odpovídá prvnímu dni -- pondělí.
print "Třetím dnem je %s." % denTydne(2)

Vraťme se opět k násobení. Co kdybychom chtěli definovat funkci, která vrací hodnoty násobení v podobě pole čísel? V jazyce BASIC by to vypadalo takto:

FUNCTION NASOBKY% (N%)
    DIM HODNOTY(12) AS INTEGER
    FOR I = 1 to 12
        HODNOTY(I) = I*N%
    NEXT I
    RETURN HODNOTY
END FUNCTION

A v jazyce Python by to vypadalo následovně:

def nasobky(n):
    # Vytvoř prázdný seznam
    hodnoty = []  
    for i in range(1, 13):
        hodnoty.append(i * n)
    return hodnoty

Psát konkrétně něco takového by bylo pěkně hloupé, protože je prostě jednodušší v okamžiku požadavku na hodnotu vypočítat i * n. Ale doufám, že jste pochopili, kam tím míříme. Funkce, která počítá slova v řetězci, již může být mnohem praktičtější. Můžete ji použít i pro zjištění počtu slov v souboru tím, že sečtete počty slov pro jednotlivé řádky.

Kód by mohl vypadat nějak takto:

def pocetSlov(s):
    seznam = split(s)  # seznam, kde prvkem je vždy slovo
    return len(seznam) # vrátíme počet prvků seznamu

for radek in soubor:
    celkem = celkem + pocetSlov(radek) # sečti počty za každý řádek
print "Soubor má %d slov." % celkem

Pokud byste uvedený kód vyzkoušeli, zjistili byste, že nefunguje. To, co jsme si právě předvedli, je běžná technika návrhu programu. Spočívá v načrtnutí našich představ o tom, jak by kód měl vypadat, ale netrápíme se s tím, aby byl kód absolutně správný. Takovému zápisu se někdy říká pseudo kód nebo — při dodržení formálnějšího stylu — zápis v jazyce pro popis programu (z anglického Program Description Language nebo zkráceně PDL).

Až se blíže podíváme na práci se soubory a s řetězci (v další části kurzu), vrátíme se k tomuto příkladu a vytvoříme jeho funkční verzi.

Funkce v Tcl

Funkce můžeme, samozřejmě, vytvářet i v Tcl. Používáme k tomu příkaz proc:

proc nasobky {m} {
    for {set i 1} {$i <= 12} {incr i} {
        lappend hodnoty [expr $i * $m]
        }
    return $hodnoty
}

Všimněte si, že použitím Tcl příkazu lappend automaticky vytvoříme seznam nazvaný hodnoty a začneme do něj přidávat prvky.

Malé varování

Jazyk Tcl je ve způsobu zacházení s funkcemi poněkud odlišný od jiných jazyků. Mohli jste si všimnout, že volané příkazy jsou zabudovanými funkcemi jazyka Tcl. V jazyce Tcl je každý příkaz, který napíšete na příkazový řádek, ve skutečnosti voláním funkce. Většina ostatních jazyků definuje množinu klíčových slov, jako například for, while, if/else a další. V jazyce Tcl se všechna tato klíčová slova pro řídicí struktury transformují na příkazy nebo na funkce. Tato skutečnost má zajímavý, matoucí a velmi mocný efekt. Umožňuje nám změnit chování zabudovaných řídicích struktur — například takto:

set i 3
while {$i < 10} {
    puts $i
    set i [expr $i + 1]
    }

Ve shodě s naším očekáváním, tento kód vytiskne čísla od 3 do 9 (což je o 1 méně, než 10). Ale nadefinujme si nyní naši vlastní verzi příkazu while:

proc while {x y} {
  puts "Toto je nyní můj příkaz while"
}

set i 3
while {$i < 10} {
    puts $i
    set i [expr $i + 1]
    }

Uvedený kód pak neudělá nic víc, než že vytiskne zprávu "Toto je nyní můj příkaz while". Podmínkový výraz a posloupnost příkazů jsou ignorovány, protože Tcl se na ně dívá jako na parametry funkce while a naše nová funkce while tyto parametry sice očekává, ale ignoruje je. Takže tady vidíte, jak lze v jazyce Tcl definovat procedury a jak je lze zneužít pro vytvoření velmi matoucích programů. Nedělejte to, pokud pro to nemáte velmi závažný důvod!

Vytváření našich vlastních modulů

Takže nyní již umíme vytvářet naše vlastní funkce a volat je z jiných částí našeho programu. Vytváření vlastních funkcí nám ušetří hodně psaní a — což je mnohem důležitější — učiní naše programy srozumitelnějšími. Je to dáno tím, že do vytvořených funkcí ukryjeme některé detaily, na které pak při jejich použití nemusíme myslet. (Tento princip obalování složitých úseků programu funkcemi se z docela zřejmých důvodů nazývá ukrývání informací [information hiding].) Ale jak můžeme tyto funkce používat v jiných programech? Vytvoříme modul.

Moduly v jazyce Python

V jazyce Python není modul ničím zvláštním. Je to jednoduše prostý textový soubor s příkazy programu v jazyce Python. Obvykle jsou to definice funkcí. Takže, když napíšeme…

from sys import *

tak tím jakoby okopírujeme obsah sys.py do našeho programu. Je to téměř jako kdybychom text modulu přenesli přes schránku (clipboard) operacemi Kopírovat a Vložit (cut and paste). (No, ve skutečnosti to takhle není, ale koncepčně to můžeme tak chápat.) Překladače některých jazyků (z význačných jmenujme C++) kopírují na základě požadavků obsah souborů s moduly do aktuálního programu doslova[1].

Takže si to zrekapitulujme. Modul vznikne vytvořením pythonovského souboru (.py), který obsahuje funkce, které chceme používat v jiných programech. Potom jednoduše provedeme import našeho modulu přesně stejným způsobem, jako to děláme se standardními moduly. Snadné, co? Tak pojďme na to.

Okopírujte si níže uvedenou funkci a uložte ji do svého souboru se jménem nasobky.py.

def tisk_tabulky(nasobitel):
    print "--- Tisk tabulky násobků číslem %d ---" % nasobitel
    for n in range(1, 13):
        print "%d x %d = %d" % (n, nasobitel, n * nasobitel)

Nyní na příkazový řádek systému Python napište:

>>> import nasobky
>>> nasobky.tisk_tabulky(12)

No vida! Právě jste vytvořili modul a použili jste ho.

Důležitá poznámka: Pokud jste Python nespustili ze stejného adresáře, ve kterém je uložen váš soubor nasobky.py, pak jej Python možná nenašel a zahlásil chybu. Pokud tomu tak skutečně je, můžete vytvořit proměnnou prostředí nazvanou PYTHONPATH, která obsahuje seznam adresářů, ve kterých se budou hledat moduly (tedy ty, které nejsou dodávány jako standardní spolu se sytémem Python).

Způsob vytváření proměnných prostředí je závislý na platformě. Předpokládám, že příslušné operace buď znáte nebo si je umíte zjistit.

Moduly v jazycích BASIC a Tcl

A jak je na tom BASIC? Tady to bude trochu složitější. Ve variantě QBASIC a v dalších starších variantách koncepce modulu vůbec neexistuje. Potřebný kód z dřívějších projektů musíte okopírovat do aktuálního projektu pomocí funkcí textového editoru. Ale ve variantě Visual Basic koncept modulu existuje. Modul můžeme načíst příkazem menu File|Open Module... v integrovaném vývojovém prostředí (IDE). Pro moduly jazyka Visual Basic existují určitá omezení v tom smyslu, že do něj nemůžeme umístit cokoliv. Do detailů se zde pouštět nebudeme, protože se zde tímto jazykem nezabýváme.

Poznámka: Pokud chcete experimentovat, můžete si ze serveru firmy Microsoft zdarma stáhnout ořezanou verzi jazyka Visual Basic, která je známá jako COM Controls Edition (CCE). Ořezaná verze VB je také součástí instalace Windows 98, 2000 a IE5. Říká se jí VBScript a pracuje se soubory končícími .vbs.

A nakonec se podíváme na Tcl. Tento jazyk si, jako vždycky, zvolil ke znovupoužití modulů (místo modulu se upřednostňuje název knihovna) trochu jinou, nicméně zajímavou cestu.

Na nejjednodušší úrovni můžete jednoduše vytvořit soubor s Tcl funkcemi — stejně, jako to děláme v jazyce Python. V našem programu pak funkce v daném souboru můžeme zpřístupnit operací source. Tím interpretu řekneme, aby přečetl daný soubor. V něm uložený kód se tak zpřístupní k dalšímu použití. Ale v Tcl existuje i zajímavější možnost.

Naše soubory můžeme po vytvoření umístit do zvoleného adresáře a poté spustíme příkaz mk_index. Ten vytvoří rejstřík (index) všech funkcí a souborů v podadresáři. V našem programu pak jednoduše voláme požadovanou funkci a interpret Tcl — když zjistí, že není dostupná — automaticky prohledá rejstřík. Nalezne v knihovně odpovídající zdrojový soubor, zpřístupní jeho kód operací source a požadovanou funkci provede.

Jakmile byla funkce již jednou zpřístupněna, zůstane přístupná i nadále, takže její následné volání nevede k vyšší výkonnostní režii. Jediný problém spočívá v tom, že programátor nemůže definovat více než jednu funkci se stejným jménem. Popsaný rys jazyka Tcl je znám jako autoloading (automatické načítání nebo automatické zavádění).

V další části se podíváme na soubory a na zpracování textu. A potom, jak jsme si slíbili, se znovu podíváme na problém počítání slov v souboru. Ve skutečnosti si nakonec pro naše potřeby vytvoříme modul s funkcemi pro zpracování textu.

Zapamatujte si

Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím . Tím bude do subjektu dopisu automaticky vložena informace o jméně a verzi tohoto HTML dokumentu.

$Id: cztutfunc.html,v 1.2 2004/05/14 10:22:46 prikryl Exp $