Podobně jako jiné jazyky, i Python obsahuje příkazy pro řízení toku programu. Kromě již zmíněného příkazu while představeného v minulé kapitole jde o další neméně významné konstrukce if, for, break, continue a především uživatelsky definované funkce. Těm všem je věnována tato kapitola.
Nejznámějším, nejjednodušším a nejpoužívanějším příkazem pro řízení toku programu je pravděpodobně příkaz if. S jeho pomocí lze zrealizovat jakýkoli jiný příkaz pro řízení toku. Implementuje nejjednodušší rozhodovací mechanismus, je-li nějaká podmínka pravdivá, vykoná určitý blok kódu. Časem se ujaly i rozšíření typu větví else a elif.
>>> x = int(raw_input("Zadejte celé číslo: ")) >>> if x < 0: ... x = 0 ... print 'Záporné číslo změněno na nulu' ... elif x == 0: ... print 'Nula' ... elif x == 1: ... print 'Jedna' ... else: ... print 'Více' ...
Příkaz if otestuje řídící podmínku a pokud je pravdivá, spustí svoje tělo. Pokud jde o jednoduchou podmínku (bez větví else), je v případě nepravdivosti podmínky řízení předáno na příkaz následující po těle příkazu if.
Pokud je příkaz if složen i z větví elif, je v případě nepravdivosti řídící podmínky otestována další podmínka v první větvi elif. Pokud je pravdivá, vykoná se tělo příslušející této větvi. Je-li nepravdivá, pokračuje se stejně s dalšími větvemi elif. V každém případě se ale po vykonání libovolného těla předá řízení až za poslední větev elif (případně else).
Příkaz if může mít i maximálně jednu větev else. Ta je spuštěna pokud nevyhovuje ani jedna řídící podmínka u větví if a elif.
Větev elif by se dala nahradit i větví else a dalším příkazem if, šlo by však o zbytečnou komplikaci, a především proto se ujala větev elif ("elif" je zkratkou pro "else if"). Jazyk Python neobsahuje příkaz typu switch, plně ho totiž nahrazuje příkaz if ... elif ... elif ... .
Příkaz for v jiných jazycích jsou poněkud odlišné od toho, jak jej poznáme v jazyce Python. Od toho v jazyce Pascal je naprosto odlišný, Pascal umožňuje řídící proměnné přiřazovat hodnoty v určitém rozsahu čísel, nanejvýš bylo možné specifikovat krok mezi jednotlivými hodnotami. Jazyk C již nabízí tři části, které se provedou -- první před samotným spuštěním cyklu, druhá určuje podmínku pro ukončení cyklu a třetí se opakuje po každém cyklu. To nabízí programátorovi široké možnosti, ne každý je však dokáže využít.
Python šel jinou cestou, obvyklou v interpretovaných jazycích. Příkaz for umožňuje iterovat prvky libovolné sekvence (připomeňme, že sekvencí jsou kromě seznamů i řetězce). Za řídící proměnnou se postupně dosazují všechny prvky sekvence v tom pořadí, v jakém jsou v sekvenci uloženy. K ukončení cyklu dojde po vyčerpání všech prvků (případně příkazem break nebo neodchycenou výjimkou):
>>> # Vytisknutí délky řetězců: ... a = ['kočka', 'okno', 'defenestrace'] >>> for x in a: ... print x, len(x) ... kočka 5 okono 4 defenestrace 12
Během cyklu není bezpečné modifikovat řídící sekvenci, by mohlo dojít buď k vynechání nebo opakování prvku (to platí pouze pro proměnné sekvenční typy, u neměnných to nelze již z jejich principu). Potřebujeme-li přesto změnit pořadí prvků v seznamu přes nějž iterujeme, musíme iterovat přes prvky kopie původního seznamu. Kopii seznamu snadno a rychle vytvoříme pomocí subsekvence s vynechanými indexy:
>>> for x in a[:]: # vytvoření kopie celého seznamu ... if len(x) > 6: a.insert(0, x) ... >>> a ['defenestrace', 'kočka', 'okno', 'defenestrace']
Je-li třeba iterovat přes prvky aritmetické posloupnosti, budeme potřebovat interní funkci range(), která vrací tuto posloupnost jako klasický seznam. Funkci range() předáme konečnou hodnotu posloupnosti. Pro všechny prvky pak bude platit, že jsou menší než tato hodnota:
>>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Programátor si rovněž může vyžádat, že chce aby posloupnost začínala jiným číslem než nulou, případně lze zvolit i jiný přírůstek než jedna (včetně záporných čísel):
>>> range(5, 10) [5, 6, 7, 8, 9] >>> range(0, 10, 3) [0, 3, 6, 9] >>> range(-10, -100, -30) [-10, -40, -70]
Chceme-li prvky aritmetické posloupnosti použít jako indexy do nějaké sekvence, je velmi vhodné zkombinovat funkce range() a len():
>>> a = ['Micinko', 'Lízinko', 'čičí', 'vylezte'] >>> for i in range(len(a)): ... print i, a[i] ... 0 Micinko 1 Lízinko 2 čičí 3 vylezte
Python podporuje mnohem více operátorů psaní podmínek než třeba jazyk C. Kromě
operátorů porovnání je možné použít i další. Především jde o test na
přítomnost/nepřítomnost prvku v určité sekvenci -- to zajišťují operátory
in
a not in
.
Další dvojice operátorů (is
a not is
) porovnává dva objekty
a vrátí logickou jedničku pokud jde (resp. nejde) o ty samé objekty (musí se
rovnat nejen jejich hodnota, ale i umístění v paměti atd.).
Všechny operátory porovnání mají stejnou prioritu, která je nižší než priorita
všech numerických operátorů, proto je možné psát výraz x + 3 > y
bez
uzávorkování.
Porovnání může být zřetězeno. Například výraz a < b == c
testuje, jestli
a
je menší než b
a zároveň b
je rovno c
.
Jednotlivé podmínky můžeme kombinovat použitím Booleových operátorů and
a or
. Libovolný logický výraz můžeme znegovat operátorem not
.
Všechny Booleovské operátory mají nejnižší prioritu ze všech operátorů
(z nich pak not
má nejvyšší, následuje and
a or
), takže
A and not B or C
je totéž, co (A and (not B)) or C
. Závorky je
možné samozřejmě použít na libovolném místě pro úpravu priority operátorů.
Booleovské operátory and
a or
mají v Pythonu tzv. zkrácené
vyhodnocování, jejich argumenty jsou vyhodnocovány zleva doprava a pokud je
možné definitivně určit výsledek výrazu, dojde k ukončení vyhodnocení. Z toho
vyplývá, že pokud A
a C
jsou pravdivé, ale B
nepravdivý,
výraz A and B and C
nevyhodnotí výraz C
.
Výsledek porovnání je možné přiřadit libovolné proměnné. Například:
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance' >>> non_null = string1 or string2 or string3 >>> non_null 'Trondheim'
V Pythonu, narozdíl od C, se nemůže vyskytovat přiřazení uvnitř výrazu.
Programátorům v C to může vadit, ale Python se tímto vyhýbá mnoha chybám, ke
kterým dojde zapsáním =
ve výrazu namísto ==
.
Zároveň s cykly je třeba zmínit i příkazy pro jejich řízení -- break a continue. Oba dva jistě znáte i z jiných programovacích jazyků.
Příkaz break ukončí aktuální cyklus for nebo while a vrátí řízení za tento příkaz. Naproti tomu příkaz continue způsobí pokračování další iterací. Zbytek těla za tímto příkazem je ignorován a program se vrátí zpět k řídící části a znovu otestuje řídící podmínku (cyklus while) nebo pokračuje dosazením dalšího prvku za řídící proměnnou (cyklus for).
Oba typy cyklů mohou mít také větev else. K jejímu spuštění dojde vždy při "regulérním" ukončení cyklu, tj. stane-li se řídící podmínka nepravdivou (cyklus while) případně vyčerpají-li se všechny prvky řídící sekvence (cyklus for). V případě ukončení cyklu příkazem break nebo neodchycenou výjimkou se větev else nevykonná! Toto chování demonstruje ukázka -- hledání prvočísel menších než deset:
>>> for n in range(2, 10): ... for x in range(2, n): ... if n % x == 0: ... print n, 'je rovno', x, '*', n/x ... break ... else: ... # cyklus proběhl bez nalezení dělitele ... print n, 'je prvočíslo' ... 2 je prvočíslo 3 je prvočíslo 4 je rovno 2 * 2 5 je prvočíslo 6 je rovno 2 * 3 7 je prvočíslo 8 je rovno 2 * 4 9 je rovno 3 * 3
Příkaz pass je trochu neobvyklý. Jedná se o prázdný příkaz, nedělá vůbec nic. Používá se obzvláště na místech, kde jazyk syntakticky vyžaduje nějaký příkaz, ale programátor žádnou akci nepožaduje:
>>> while 1: ... pass # činné čekaní na přerušení ...
V Pythonu, stejně jako v jiných jazycích, můžeme používat množství funkcí. Každý jazyk také umožňuje programátorovi definovat si své vlastní funkce. Do těchto uživatelsky definovaných funkcí lze soustředit izolované kusy kódu s určitým rozhraním. Například si můžeme napsat funkci, jenž vytiskne Fibonacciho rozvoj do zadané meze:
>>> def fib(n): # vypiš Fibonacci rozvoj do n ... """Vytiskne Fibonacciho rozvoj do n.""" ... a, b = 0, 1 ... while b < n: ... print b, ... a, b = b, a+b ... >>> # Nyní zavoláme funkci tak, jak jsme si ji definovali: ... fib(2000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
Definice funkce je uvozena klíčovým slovem def. Toto klíčové slovo následuje jméno funkce a výčet formálních argumentů. Následující příkazy, tvořící tělo funkce, jsou zapsány na dalších řádkách. Tělo funkce je blok kódu a proto musí být odsazeno podobně jako tělo cyklu apod. Nepovinnou součástí těla funkce může být i tzv. dokumentační řetězec. Tento řetězec je uvedený bezprostředně po hlavičce funkce.
Existuje mnoho nástrojů, které používají dokumentační řetězce k automatickému generování dokumentace. Některé z nich umožňují uživateli interaktivně procházet kód programu, přičemž pro lepší orientaci zobrazují právě tyto dokumentační řetězce. Je proto dobrým zvykem psát dokumentační řetězce ke každé funkci, kterou napíšete. Kromě funkce lze dokumentační řetězce uvádět i u dalších objektů -- u tříd, metod nebo modulů.
Zavoláním funkce se vytvoří nový prostor jmen používaný pro lokální proměnné této funkce. To znamená, že všechna přiřazení uvnitř těla funkce jdou do tohoto lokálního prostoru jmen. To zabraňuje přímému přiřazení hodnoty globální proměnné (tj. proměnné definované mimo funkci). Použijeme-li některou proměnnou v libovolném výrazu, je její jméno hledáno nejprve v lokálním oboru jmen a teprve pokud tam není nalezeno, je prohledán globální obor jmen (a pokud není nalezeno ani tam je nakonec prohledán interní obor jmen, není-li toto jméno nalezeno vůbec, dojde k výjimce). Je důležité podotknout, že přestože globální proměnné není možné z těla funkce modifikovat, je stále možné je číst. (Pokud deklarujeme v těle funkce nějakou proměnnou jako globální příkazem global, je možné jí takto používat, čili je možná i její modifikace kódem funkce).
Skutečné argumenty předané funkci při jejím zavolání jsou uloženy v lokálním prostoru jmen této funkce. Argumenty jsou předávány hodnotou.4.1 Jestliže volaná funkce zavolá jinou funkce, je opět pro toto volání vyhrazen nový prostor jmen. To zabrání vzájemnému ovlivňování funkcí, které by zákonitě nastalo, pokud by sdílely stejný prostor jmen.
Definice funkce vytvoří novou proměnnou, jejíž jméno je stejné jako jméno funkce a je uloženo v lokálním prostoru jmen. Její hodnota má speciální typ -- uživatelsky definovaná funkce. Tato hodnota může být přiřazena jiné proměnné, kterou poté lze použít pro volání této funkce. Tímto způsobem lze libovolný objekt přejmenovat.
Zde se opět uplatňuje mechanismus odkazů na objekty. Objekty existují nezávisle na svých jménech. Jméno objektu je naprosto irelevantní, jeden objekt může vystupovat pod různými jmény. Dokonce objekt nemusí být pojmenovaný vůbec, to nastává v případě, že objekt se stane součástí třeba seznamu. Jména objektů slouží pouze pro jednoduchou orientaci programátora, jednoduše pojmenovávají objekty a umožňují snadný přístup k nim.
>>> fib <function object at 10042ed0> >>> f = fib >>> f(100) 1 1 2 3 5 8 13 21 34 55 89
Programátoři v jazyce Pascal zřejmě namítnou, že funkce fib
není funkcí,
ale procedurou. Python, podobně jako C, považuje procedury za speciální druh
funkce, která nevrací žádnou hodnotu. Ve skutečnosti procedury jazyka Python
vrací hodnotu None
. Tato hodnota je obdobou hodnoty null
v jazyce
C. Její vypsání je ale potlačeno interpretrem, pokud tuto návratovou hodnotu
chcete vidět, musíte si o to říci sami:
>>> print fib(0) None
Velice jednoduše lze napsat funkci, která, místo aby vytiskla výsledek, vrátí Fibbonacciho rozvoj jako seznam čísel:
>>> def fib2(n): # vrátí Fibonacciho rozvoj do čísla n ... """Vrátí seznam obsahující Fibonacciho rozvoj do čísla n.""" ... vysledek = [] ... a, b = 0, 1 ... while b < n: ... vysledek.append(b) # viz níže ... a, b = b, a+b ... return vysledek ... >>> f100 = fib2(100) # zavoláme funkci fib2() >>> f100 # a vytiskneme její výsledek [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Tato ukázka nám názorně ukazuje další nové vlastnosti jazyka Python:
Samotný příkaz return vrátí None
podobně jako vykonání
všech příkazů těla funkce.
vysledek.append(b)
znamená zavolání metody seznamu
vysledek
. Metoda je zvláštní funkce, která určitým způsobem "patří"
k určitému objektu. Její funkcí je většinou nějak modifikovat samotný
objekt. Proto každý objekt má svoje vlastní metody, každá modifikuje pouze
svůj vlastní objekt a ostatní nechá na pokoji. Různé objekty mohou
obsahovat různé metody. Dokonce lze, aby metody různých objektů měly stejná
jména a přitom prováděly naprosto jinou činnost. Za použití
tříd4.2 je možné definovat vlastní typy objektů a jejich metody. Metodu
append() definují objekty typu seznam. Ta při svém vykonání přidá
na konec seznamu prvek, který jí byl předán jako jediný argument. Odpovídá
výrazu "vysledek = vysledek + [b]" ale je mnohem efektivnější.
Funkci lze definovat s mnoha různými argumenty. Python nabízí i několik speciálních zápisů, které programátorovi značně usnadňují život. Navíc všechny tyto speciální zápisy mohou být libovolně mezi sebou kombinovány.
Velice užitečné je specifikování implicitních hodnot argumentů. Při volání funkce je pak možné tyto argumenty vynechat. V tomto případě jim bude zcela automaticky přiřazena implicitní hodnota. Typickým příkladem může být funkce, jenž zobrazí výzvu a čeká na odpověď uživatele:
def ano_ne(vyzva, opakovani=4, upozorneni='Ano nebo ne, prosím'): while 1: ok = raw_input(vyzva) if ok in ('a', 'an', 'ano'): return 1 if ok in ('n', 'ne'): return 0 opakovani = opakovani - 1 if opakovani < 0: raise IOError, 'uživatel ignorant' print upozorneni
Tato funkce může být volána jako
ano_ne('Chcete opravdu ukončit program?')
případně jako
ano_ne('Přejete si prepsat soubor?', 2)
.
Implicitní hodnoty argumentů jsou vyhodnoceny při definici funkce, přičemž jako jmenný prostor (místo, kde se vyhledávají jména proměnných) je použit prostor jmen definujícího bloku kódu takže kód
i = 5 def f(arg=i): print arg i = 6 f()
vytiskne číslo 5
.
Implicitní hodnoty argumentů jsou vyhodnoceny pouze jednou, i při opakovaném volání funkce se používá stále tentýž objekt. To má význam v případě proměnných objektů. Názornou ukázkou může být funkce shromažďující argumenty, které jí byly předány při jednotlivých voláních, načež vrátí seznam těchto argumentů:
def f(a, L=[]): L.append(a) return L print f(1) print f(2) print f(3)
Předchozí blok kódu potom postupně vytiskne následující výstup:
[1] [1, 2] [1, 2, 3]
V některých případech však může být toto sdílení hodnot mezi voláními překážkou.
Řešením tohoto problému může být použití nějaké známé implicitní hodnoty argumentu (třeba None
)
a následné testování hodnoty argumentu na tuto hodnotu. Pokud je argument roven
známé implicitní hodnotě, přiřadí se mu nová hodnota (proměnný objekt, u něhož
si nepřejeme sdílení), není-li argument roven této hodnotě, použije se předaná
hodnota:
def f(a, L=None): if L is None: L = [] L.append(a) return L
Pokud používáme implicitní hodnoty argumentů, často potřebujeme specifikovat hodnotu pouze jednoho argumentu. Pokud je tento argument první ze všech, je vše bez problémů. Potíž nastává v případě, že chceme specifikovat hodnotu jiného argumentu.
Samozřejmě by se nechalo do volání vepsat implicitní hodnoty předcházejících argumentů. Problém by však nastal v případě změny hodnoty jednoho z implicitních argumentů. Následné dohledání všech volání by bylo velice těžké.
Python proto může přiřadit hodnotu určitému formálnímu argumentu. Jednoduše do volání funkce napíšeme jméno tohoto argumentu následované rovnítkem a jeho hodnotou (čili "funkce(jméno_formálního_parametru = hodnota)". V tomto případě se formálnímu argumentu přezdívá keyword argument. Keyword argumenty můžeme použít i v případě, kdy nepoužíváme implicitní hodnoty argumentů.
Pokud kombinujeme keyword a klasické (poziční) argumenty, je třeba důsledně dbát na to, aby poziční argumenty byly uvedeny před keyword argumenty. Zároveň je třeba splnit požadavek předání hodnot všem argumentům, které nemají implicitní hodnotu (tj. jde o povinné argumenty). Použijeme-li neznámý keyword argument, dojde při volání funkce k chybě. Je třeba se také vyhnout předání hodnoty jednomu argumentu dvakrát (jednou jako pozičnímu argumentu, podruhé jako keyword argumentu).
Následuje příklad použití keyword argumentů. Funkci definovanou jako
def papousek(napeti, xxx='Python', akce='zpívat', keczy='bla, bla, bla'): print "-- Tento papoušek nebude", akce, print "když do něj pustíte", napeti, "Voltů." print "-- A nějaké kecy:", keczy print "-- A ještě něco:", xxx, "!"
můžeme volat následujícími způsoby:
papousek(1000) papousek(akce = 'řvát', napeti = 1000000) papousek('tisíc', xxx = 'Chudák papouch') papousek('milion', 'Papoušek to nerozchodí', 'skákat')
Všechna následující volání jsou ale chybná:
papousek() # chybějící povinný argument papousek(napeti=5.0, 'ahoj') # povinný argument následující po keyword papousek(110, napeti=220) # hodnota argumentu předána dvakrát papousek(vrah='Pepa Zdepa') # neznámý keyword argument
Pokud při volání funkce specifikujeme více pozičních argumentů, než samotná
funkce požaduje, dojde při jejím volání k chybě. Proto je možné v hlavičce
funkce specifikovat speciální formální argument s předponou *
(např.
*arg
). Tomuto argumentu budou přiřazeny všechny přebývající argumenty,
předané při volání funkce (typem tohoto argumentu je tuple). Pokud žádné takové
argumenty nejsou předány, nabude tento argument hodnotu prázdné
tuple.4.3. Funkce, jenž tuto možnost určitě využije, je obdoba céčkovské funkce
fprintf:
def fprintf(soubor, format, *argumenty): file.write(format % argumenty)
Kromě argumentu typu *arg
je možné použít i argument ve tvaru **kwarg
,
kterému bude přiřazeno asociativní pole keyword argumentů, jejichž jména
neodpovídají definici funkce. Pak můžeme funkci definovanou jako
def big_burger(druh, *argumenty, **keyword): print "-- Máte", druh, '?' print "-- Promiňte,", druh, "došly." for arg in argumenty: print arg print '-'*40 for kw in keyword.keys(): print kw, ':', keyword[kw]
volat následujícími způsoby:
big_burger('cheesburgery', "Velice se omlouvám, pane.", "Opravdu se velice se omlouvám, pane.", zakaznik='Adam', prodavac='Bedřich', obchod='Honzův domácí BigBurger')
Jejím výstupem pak bude text
-- Máte cheesburgery ? -- Promiňte, cheesburgery došly. Velice se omlouvám, pane. Opravdu se velice se omlouvám, pane. ---------------------------------------- obchod : Honzův domácí BigBurger prodavac : Bedřich zakaznik : Adam
Oba speciální typy argumentů lze kombinovat, pak musí argument *arg
předcházet argumentu **kwarg
.
Často je potřeba napsat funkci, která vykonává velice jednoduchý příkaz. Ve funkcionálních programovacích jazycích (a nejen v nich) se proto ujaly tzv. lambda funkce, tj. krátké funkce, většinou bezejmenné, určené pro vykonávání jednoduchých příkazů.
V Pythonu je možné lambda funkce také používat. Nejjednodušší ukázkou může být funkce vracející součet dvou argumentů: "lambda a, b: a+b". Lambda funkce v Pythonu je výraz, proto, abychom zachovali její hodnotu, musíme jí přiřadit nějaké proměnné (případně předat nějaké funkci apod.):
>>> soucet = lambda a, b: a+b >>> print soucet(1, 2) 3
Lambda funkce v Pythonu jsou tvořeny klíčovým slovem lambda, výčtem argumentů, dvojtečkou a samotným tělem funkce. Je třeba podotknout, že tělo funkce je tvořeno jediným výrazem. Ten je při zavolání funkce vyhodnocen a jeho hodnota je zároveň návratovou hodnotou lambda funkce. Toto omezení je třeba respektovat, v těle lambda funkce nelze použít příkazy typu print atd. Podobně jako je tomu u vložených funkcí, i lambda funkce se mohou odkazovat na proměnné nadřazené funkce:
>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 43
Jak jsme si již řekli, je dobrým zvykem psát dokumentační řetězce ke každé funkci, modulu nebo třídě, které vytvoříme. Nyní si povíme o tom jak psát a formátovat dokumentační řetězce.
Na prvním řádku dokumentačního řetězce by měla být krátká věta shrnující účel celého objektu. Neměla by explicitně pojmenovávat objekt, jeho jméno je přístupné jinou cestou. Jako každá věta by měla začínat velkým písmenem a končit tečkou.
Je-li třeba rozsáhlejší dokumentační řetězec, je možné použít víceřádkový řetězec. V tomto případě by měla být druhá řádka prázdná, oddělující souhrn na první řádce od zbytku popisu. Ten by může být tvořen i více odstavci. Ty se mohou týkat volajících konvencí používaných u objektu, vedlejších efektů tohoto objektu apod.
Parser interpretru neodstraňuje (!) odsazení z víceřádkových řetězců, proto by měly nástroje, které generují dokumentaci z dokumentačních řetězců, toto odsazení odstranit. Přitom lze použít jednoduchý postup, nejprve se nahradí tabulátory odpovídajícím počtem mezer (8). Poté se určí odsazení. Protože z prvního řádku řetězce toto odsazení nelze zjisti, použije se první neprázdný řádek dokumentačního řetězce. O toto zjištěné odsazení se poté zkrátí každý řádek řetězce. Pokud se v dokumentačním řetězci nachází řádek, který je odsazen méně, než činí průběžné odsazení, jsou z jeho začátku odsazeny všechny bílé znaky.
Zde je příklad víceřádkového dokumentačního řetězce, jehož by se případná úprava týkala. Jak vidíte, druhý řádek textu je opravdu odsazen:
>>> def moje_funkce(): ... """Nic nedělá, ale zdokumentujeme ji. ... ... Nedělá opravdu nic. ... """ ... pass ... >>> print moje_funkce.__doc__ Nic nedělá, ale zdokumentujeme ji. Nedělá opravdu nic.