title end end

5. Opakované výpočty

  1. Funkce print
  2. Příkaz return
  3. Smyčka for..
  4. Podmínka while
  5. Podmínky if.. elif.. else
    1. Podmínka if ..
    2. Podmínka if .. else
    3. Podmínka if .. elif .. else
    4. Procedura if_case(choice)
    5. Vnořené podmínky
    6. Příkaz break
    7. Příkaz continue
  6. Funkce range
  7. Funkce enumerate
  8. Funkce filter, map a zip
  9. Funkce functools.reduce
  10. Anonymní funkce
  11. Glosář
  12. Cvičení

5.1 Funkce print()

Funkce print() se všemi svými parametry má tento tvar:

print(*objekty, sep="", end="\n", file=sys.stdout, flush=False)

Kdo si přečetl odstavec 4.2 Parametry a argumenty, ví že parametr *objekty je sběrný enticový parametr a všechny ostatní parametry lze ignorovat, neboť to jsou parametry s počáteční hodnotou, neboli párové parametry.
Takže se nám povinný tvar volání funkce smrskne na:

print(*objekty)
přičemž i tento parametr lze vynechat a v tom případě nám funkce vytiskne prázdný řádek.
Doporučovanou variantou print() je příkaz:
>>> print("\n")                  # vytiskne 1 prázdný řádek        
Případně
>>> print(3*"\n")                # vytiskne 3 prázdné řádky

Funkce print vyvolá vyhodnocení jednotlivých argumentů v entici:

>>> print("Je mi", 12 + 9, "let.")
Je mi 21 let.            # fce print() odebrala  apostrofy 
>>>

Párové argumenty file=sys.stdout a flush=False zajišťují výstup tisku do okna konzoly.
Změnou argumentu end="\n" na end=" " lze zajistit pokračování tisku na témže řádku, přičemž rozestupem uvozovek lze ovlivnit rozestup mezi jednotlivými hodnotami výstupu.
Změnou argumentu sep="" lze přikázat typ oddělovače, případně vzdálenost mezi prvky entice - například sep=' ~ ':

>>> print('a', 'b', 'c', 'd')
a b c d
>>> print('a', 'b', 'c', 'd', sep=' ~ ', end=' !!')
a ~ b ~ c ~ d !!       

Vhodné je také vědět, že příkaz k tisku funkce print() vrací 'None':

>>> print(print("Nazdar"))
Nazdar
None

5.2 Příkaz return

Ve funkci s větvenými podmínkami s výhodou použijeme příkaz return:

def filtr(x):                       
    if x <= 0:                            # = P
        return "Přirozené číslo, prosím"  # if P is True
    return x                              # if p is False

Protože jsou tyto příkazy v alternativních podmínkách, provedou se pouze jednou - jakmile se jeden z příkazů return provede, funkce končí bez provádění následných příkazů.

Kód, který je zapsán za příkazem return i kterékoliv další místo, kam se tok programu nedostane, se nazývá mrtvý kód.

Je dobré aby každá možná cesta funkcí narazila na příkaz return. V následující verzi absolute_value toto zajištěno není:

def absolute_value(x):
    if x < 0:
        return -x
    elif x > 0:
        return x

Tato verze není zcela správná, protože bude-li x rovno nule, žádná z podmínek není pravdivá a funkce končí, aniž by narazila na příkaz return; výslednou hodnotou je speciální hodnota zvaná None typu NoneType:

>>> print(absolute_value(0))
None

Příkaz return se použije uvnitř funkce, nikoliv uvnitř definice třídy.


5.3 Smyčka for

Smyčka for .. in si pro zadaný kontejner (sekvenci či kolekci) vytvoří vlastní iterátor, s nímž provádí iteraci po prvcích objektu:

for i in container: do something	
st = {3.6, "cat", True}                 # kolekce typu set

for i in st:
    print(i, end=" ")
True 3.6 cat 

Na výstupu idiomu for vidíme, že pořadí elementů se změnilo, protože kolekce set pořadí změnit může, stejně jako kolekce frozenset.

Iterace slovníku (dict) vrací pouze klíče slovníku:

sl = {1: "uno", 2: "duo", 3: "tre"}    # kolekce typu dict

for i in sl:
    print(i, end=" ")
1 2 3 

Případně:

sl = {1: "uno", 2: "duo", 3: "tre"}    # kolekce typu dict

for i,j in sl.items():
    print(i,j, end=" ")
1 uno 2 duo 3 tre  

O tom, že naše kontejnery jsou vybaveny metodou __iter__ a že to tedy jsou iterábly, se přesvědčíme funkcí

>>> dir(zkoumaný_objekt)

jejímž výstupem je seznam metod a funkcí zadaného objektu, v němž metodu __iter__ nalezneme.

Stejně jako smyčka for.. si vlastní iterátor vytvářejí funkce open(), min(), max(), map(), filter(), zip(), enumerate() a reversed().

Funkce open(...) s vlastním iterátorem:

>>> f = open("F:/Codetest/Abakus/week.txt")  # iterátor pro week.txt 

>>> next(f)                           # první řádek souboru
'pondělí\n'
>>> next(f)                           # další řádek souboru
 úterý                                 
>>> for i in f: print(i, end=" ")     # zbývající řádky souboru
 středa
  ...

Následující příklad ukazuje použití zřetězení (concatenation) a smyčku for pro tvorbu abecedních řad. Abecedním je míněno uspořádání prvků řady či seznamu podle abecedy. Například, v knize Roberta McCloskeyho: Uvolněte cestu pro káčata se kachňata jmenují: Jack, Kack, Lack, Mack, Nack, Ouack, Pack a Quack. Tato smyčka vydá jejich jména v pořadí:

prefixes = "JKLMNOPQ"
suffix = "ack"

for leclr-tter in prefixes:
    print(leclr-tter + suffix, end=" ")

Výstup programu je tento:

Jack  Kack  Lack  Mack  Nack  Oack  Pack  Qack

V pořádku to úplně není, protože Ouack a Quack nejsou zapsáni správně. Tuto chybu napravíme v rámci cvičení 6.17.1.


5.4 Podmínka  while

Opakovaný výpočet pro jednotlivé elementy iteráblu lze s výhodou provést pomocí smyčky while:

Použití příkazu while si ukážeme na funkci countdown:

def countdown(n):      # parametr n jako počáteční stav počítadla
    while n > 0:                    # omezující podmínka
        print(n)
        n = n-1                     # změna stavu počítadla

Příkaz while se dá číst téměř jako prostý text: Pokud bude n > 0, vytiskni hodnotu n a potom ji zmenši o 1. Když se dostaneš k hodnotě n = 0, hodnocení podmínky končí.

Formálněji popíšeme tok výpočtu s příkazem while takto:

  1. Vyhodnoť podmínku while s výstupem False nebo True.
  2. Je-li hodnocení False, vystup ze smyčky while a pokračuj dalším příkazem (pokud existuje).
  3. Je-li hodnocení True, proveď všechny příkazy v těle podmínky a vrať se ke kroku 1.

Tělo podmínky tvoří všechny příkazy pod záhlavím podmínky se stejným odsazením.

Tento typ toku programu se nazývá smyčka, protože se opakovaně vracíme k počátku. Uvědomme si, že když je podmínka nepravdivá již při prvním běhu smyčkou, příkazy uvnitř smyčky se nikdy neprovedou.

Tělo smyčky by mělo měnit hodnotu jedné či více proměnných tak, aby se podmínka nakonec stala nepravdivou a smyčka skončila. Jinak by se opakovala do nekonečna, a byla by to nekonečná smyčka.

V případě funkce countdown(n) můžeme dokázat, že smyčka končí, protože víme, že hodnota n je konečná a že se při každém průchodu smyčkou zmenšuje, takže nakonec se musíme dostat k nule. V jiných případech to tak zřejmé být nemusí.

def sequence(n):
    while n != 1:
        print(n, end=", ")
        if n%2 == 0:             # n je sudé  
            n = n//2
        else:              
            n = n*3+1
>>> sequence(4)
4, 2,
>>> sequence(3)
3, 10, 5, 16, 8, 4, 2, 

Podmínkou pro tuto smyčku je n!=1, takže bude rotovat tak dlouho, dokud nebude n=1, čímž se stane podmínka nepravdivá.

Při každém průchodu smyčkou program vrácí hodnotu n a zkontroluje, je-li sudá či lichá. Je-li sudá, je n děleno 2. Je-li lichá, nahradí se hodnotou n*3+1 - viz "sequence(3)".

Protože se n někdy zvětšuje, jindy zmenšuje, nemáme zřejmý důkaz, že n někdy dospěje k 1, nebo že program skončí. Pro jisté hodnoty n můžeme ukončení dokázat. Například, bude-li počáteční hodnotou mocnina 2, bude n stále sudé, dokud nedospěje k 1. V předchozím případě výsledná posloupnost takovou řadu (počínající číslem 16) obsahuje.

Odhlédneme-li od zmíněných čísel, je na místě otázka, zda umíme dokázat, že tento program je konečný pro všechny možné hodnoty n.   Zatím se to nikomu nepodařilo!

Jiným příkladem použití smyčky while je traverzování po elementech seznamu:

trampol.py                   # Výběr slov se sudým pořadím

slova = ["Copak", "je", "to", "za", "fešáka", "?"]

i = 0
délka = len(slova) 
while i < délka :
    if i % 2 == 1 :
        print(slova[i], end=", ") 
    i += 1
>>> %Run trampol.py
je, za, ?, 

Následující ukázka demonstruje použití pojmenovaného přiřazení (walruss operator) v kombinaci s podmínkou while a vstupem uživatele:

while (user_input := input('Enter q or p: ')) != 'q':
    if user_input == 'p':
        print("Hello!")
else: print("Quit!")
>>> Enter q or p: p
Hello!
>>> Enter q or p: 
Quit!

5.5   Podmínky if.. elif.. else

K napsání užitečného programu potřebujeme mít možnost měnit chování programu v závislosti na splnění či nesplnění jistých podmínek.
Tyto podmínky se formulují pomocí relačních (kap. 2.6.4) a logických (kap. 2.6.5) operátorů .

5.5.1 Podmínka if..

Tuto jednoduchou podmínku je vhodné použít jako součást jiné procedury, například funkce:

def odmocnina(x):
   if x >= 0:
      print ("odmocnina", x, "=", x**0.5)   

Stejně jako funkce a jiné složené příkazy se příkaz if skládá ze záhlaví a těla. Záhlaví začíná klíčovým slovem if, pokračuje booleovským výrazem neboli podmínkou a končí dvojtečkou.
Poté, co se podmínka vyhodnotí jako pravdivá, provede se odsazená část složeného příkazu.

>>> odmocnina(5)
odmocnina 5 = 2.23606797749979

Varianta s úpravou desetinného čísla pomocí f-stringu (viz 6.12):

def odmocnina(x):
   if x >= 0:
      y = x**0.5
      print (f"odmocnina {x} = {y:.2f}")
>>> odmocnina(5)
odmocnina 5 = 2.24

5.5.2 Podmínka if .. else

Touto podmínkou realizujeme takzvané alternativní provedení, při němž existují dvě možnosti a podmínka určí, která z nich se provede:

x = int(input("Zadej celé číslo: "))   # ošetření vstupu z konzoly

if x % 2 == 0:                  # při splnění podmínky
    print (x, "je sudé")        # se provede odsazený příkaz
else:                           # při nesplnění podmínky
    print (x, "je liché")       # se provede tento příkaz 
>>> Zadej celé číslo: 15
15 je liché

Je-li zbytek po dělení dvěma roven nule, potom víme, že x je sudé a program tuto zprávu zobrazí. Není-li tato podmínka pravdivá, provede se druhá sada příkazů. Protože podmínka může být pouze pravdivá nebo nepravdivá, provede se určitě jedna z obou možností.

Alternativám říkáme větve protože jsou rozvětvením toku programu.

Mimochodem, kdybychom potřebovali často posuzovat "lichost" či "sudost" čísel, mohli bychom si tento prográmek "zabalit" do funkce:
def print_parity(x):
    if x%2 == 0:                    # % je operátor modulo
        print(x, "je sudé")
    else:
        print(x, "je liché")

Podmínku if .. else lze také uplatnit jako infix mezi dvěma výstupy:

def print_parity(x):
    print(x, " je sudé") if x%2 == 0 else print(x, " je liché")    

Pro každou hodnotu x vrátí print_parity příslušnou zprávu. Při volání můžeme zadat jako argument jakýkoliv celočíselný výraz:

>>> print_parity(41)
41 je liché
>>> y = 41
>>> print_parity(y + 1)
42 je sudé

5.5.3 Podmínka if .. elif .. else

Někdy jsou více než dvě možnosti a my potřebujeme více než dvě větve. Použijeme seriově uspořádané (zřetězené) podmínky:

if x < y:
   print(x, "is less than", y)
elif x > y:
   print(x, "is greater than", y)
else:
   print(x, " and ", y, " are equal")

elif je zkratka "else if". Opět je provedena pouze jedna větev. Počet elif není omezen, příkaz else však smí být pouze jeden a musí být uveden jako poslední.

Podmínky jsou zkoumány jedna za druhou. Je-li první nepravdivá, prověřuje se další. Je-li některá pravdivá, provede se bezprostředně následující větev programu. Jestliže je pravdivých podmínek více, provede se jen první z nich.

5.5.4 Procedura if_case(choice)

Sekvencí podmínek if.., elif.., elif.., else lze simulovat v Pythonu chybějící proceduru switch case.

Mějme skript s funkcemi fce_a, fce_b, fce_c a s funkcí if_case(choice):

def fce_a():
    print("fce_a byla volána")
def fce_b():
    print("fce_b byla volána")
def fce_c():
    print("fce_c byla volána")

def if_case(choice):
    if   choice == 'a': fce_a()
    elif choice == 'b': fce_b()
    elif choice == 'c': fce_c()
    else: print("Neplatná volba")
Výstupem v konzole interpreta by mělo být:
>>> if_case("c")
fce_c byla volána
>>> if_case(5)
Neplatná volba

5.5.5   Vnořené podmínky

Jedna podmínka může být vnořena do druhé. Předchozí trojité větvení můžeme realizovat také takto:

if x == y:
    print(x, "and", y, "are equal")
else:
    if x < y:
        print(x, "is less than", y)
    else:
        print(x, "is greater than", y)

Vnější podmínka má dvě větve. První větev obsahuje prostý příkaz k tisku. Druhá větev obsahuje další podmínku if, která má své vlastní dvě větve. Obě tyto větve jsou prostým voláním funkce print.

I když odsazení příkazů činí strukturu zápisu zřejmou, vnořené podmínky se velmi rychle stávají méně přehledné. Pokud můžeme, tak se jim raději vyhneme.

Vnořené podmínky můžeme často zjednodušit logickými operátory. Následující kód bude v další ukázce přepsán pro použití jen jedné podmínky:

if 0 < x:
    if x < 10:
        print("x is a positive single digit.")

K volání funkce print dojde pouze tehdy, splníme-li obě podmínky (0 < x < 10), můžeme tedy použít operátor and.

if 0 < x and x < 10:
    print("x is a positive single digit.")

5.5.6   Příkaz break

Při splnění podmínky ukončí příkaz break provádění smyčky. Tento příkaz lze použít u smyček for  i  while:

for letter in "Python":
    if letter == "h":
        break
    print("Current Letter: ", letter)
Current Letter:  P
Current Letter:  y
Current Letter:  t

nebo:

var = 8
while var > 0:
    print("Current var. value: ", var)
    var = var - 1  
    if var == 5:
        break
print("Adieu!")
Current var. value:  8
Current var. value:  7
Current var. value:  6
Adieu!

5.5.7   Přikaz continue

Při splnění podmínky přeruší příkaz continue provádění smyčky a její výpočet pokračuje dalším elementem sekvence. Tento příkaz lze použít u smyček for i while:

for letter in "Python":
    if letter == "h":
    print("        Jsme přerušeni!")
        continue
    print("Current Letter: ", letter)  
Current Letter:  P
Current Letter:  y
Current Letter:  t
        Jsme přerušeni!
Current Letter:  o
Current Letter:  n

nebo:

var = 6
while var > 0:
    var = var - 1   
    if var == 3:
    print("        Jsme přerušeni!")
        continue
    print("Current var. value: ", var)
print("Adieu!")
Current var. value:  5
Current var. value:  4
        Jsme přerušeni!
Current var. value:  2
Current var. value:  1
Current var. value:  0
Adieu!

5.6 Funkce range

Vestavěná funkce

   range(n)                  # jen koncová mez       
   případně: 
   range([m,] n [, p])       # start  stop  krok

vytvoří potenciální číselnou posloupnost od 0 po n-1, případně od m po n-1, případně od m po n-1 s krokem p. Argumenty m a p jsou nepovinné.
Slovo potenciální označuje tu skutečnost, že místo celé zadané řady (celých) čísel se při deklaraci uloží pouze její parametry a k vlastnímu vytvoření této posloupnosti dojde až při jejím volání. Jedná se o případ takzvaného líného (lazy) vytvoření iterovatelného objektu.
Předností funkce range() je skutečnost, že zabírá stejné místo v paměti - bez ohledu na délku deklarované řady čísel.

Tuto posloupnost lze použít jako argument při tvorbě prostého seznamu, entice, setu a sekvence bytes:

>>> list(range (7))              # chápáno jako range(0,7)
[0, 1, 2, 3, 4, 5, 6]
>>> tuple(range (2,7))           # tuple je naše entice
(2, 3, 4, 5, 6)
>>> set(range (0,7,2))
{0, 2, 4, 6}
>>> bytes(range (0,7,2))        
b'\x00\x02\x04\x06'
>>> list(range (7,5))            # bez vhodného kroku vždy prázdný
[]
>>> list(range (7,0,-3))         # vida
[7, 4, 1]

Obrácenou posloupnost lze vytvořit i bez obrácených mezí a záporného kroku pomocí funkce reversed(). Tento postup nechodí pro objekt typu set a frozenset.

>>> list(reversed(range(7)))
[6, 5, 4, 3, 2, 1, 0]
>>> list(reversed(range(0,7,2)))
[6, 4, 2, 0]

Objekt typu range je indexovatelným (subscriptable) objektem. Jeho elementy jsou individuálně přístupné uvedením indexu v hranaté závorce i metodou __getitem__():

>>> range(7)[2]
2
>>> range(7).__getitem__(2)
2

Iterátor lze z objektu range vytvořit běžným způsobem funkcí iter(range) (range.__iter__() ) nebo smyčkou for..., která si iterátor vytvoří sama (sama si jej však také zkonzumuje). Iterace takovéhoto iterátoru je dychtivá (eager).

>>> for i in range(7):                # eager iteration
        print(i, end= ", ")
0, 1, 2, 3, 4, 5, 6,
for number in range(20):              # eager iteration
    if number%3 == 0:
        print(number, end="  ")
0  3  6  9  12  15  18

5.7 Funkce enumerate

Volání funkce enumerate(iterable, start=0) vrací objekt typu enumerate jenž je iterátorem, disponujícím metodou __next__() a funkcí next():

>>> fruits = ('banana','apple','pear','grape')  # iterábl
>>> enum = enumerate(fruits)                    # iterátor
>>> type(enum)                   # --> <class 'enumerate'>

Objekt typu enumerate není indexovatelný (není to iterábl, je to však iterátor), jeho elementy nejsou přímo přístupné prostřednictvím indexu v hranatých závorkách.

>>> enum[1]
# TypeError: 'enumerate' object is not subscriptable

Přístup k prvkům iterátoru lze realizovat postupně prostřednictvím iterační funkce next nebo smyčky for::

>>> next(enum)
(0, 'banana')
>>> enum.__next__()
(1, 'apple')

>>> for i in enum:
        print(i, end=" ")
(2, 'pear') (3, 'grape')                  # zbytek iterace	

Jak vidno, next(enum) i smyčka for vrací index a jemu příslušnou hodnotu. Index implicitně počíná nulou. Počáteční index lze změnit zadáním jiné hodnoty pro parametr start.

Funkci 'enumerate' lze použít pro vyjádření vztahu argument - upravený argument:

numbers = [2, 3, 4, 5]
for elem, value in enumerate(numbers):      
    print(numbers[elem], " -> ", value**2)
2  ->  4
3  ->  9
4  ->  16
5  ->  25

5.8 Funkce filter, map a zip

Všechny tyto tři funkce (vyššího řádu) pracují s iterovatelnými objekty (iterábly - viz 3.2) a produkují iterátory.

Funkce filter(function, iterable) extrahuje ty prvky ze zadaného iteráblu (list, string, tuple, set, ...), pro něž zadaná funkce s podmínkou vrací hodnotu True. Jako funkce se často používá anonymní funkce lambda.

Funkce filter() vrací iterátor, jenž musíme vhodným způsobem rozbalit, například funkcí list(), tuple(), set():

>>> ages = [7, 10, 16, 18, 26, 35]            # iterábl
>>> adults = filter(lambda x: x > 17, ages))  # iterátor
>>> print("Adults: ", list(adults))     # nutno rozbalit
Adults:  [18, 26, 35]
# Případně iterátor rozbalíme hned:
>>> not_adults = tuple(filter(lambda x: x <= 17, ages))
>>> print("Not adults: ", not_adults)
Not adults:  (7, 10, 16)

Iterátor lze rozbalit i smyčkou for ... :

>>> for i in filter(lambda x: x % 2 == 0, ages):
    print(i)
10 16 18 26	

Pro přetvoření iteráblu lze použít i formát funkce:

>>> def is_even(x):
    return x % 2 == 0             # vrací True nebo False                  
>>> print(set(filter(is_even, ages)))
{10, 16, 18, 26}	

Funkce map(fce, iterable, ...) aplikuje zadanou transformační funkci na každý element zadaného iteráblu (seznamu, entice, atp) a vrací sekvenci výsledků (objekt map, což je iterátor, který nutno rozbalit).

numbers = 1, 2, 3, 4                 # iterábl
def squares(n):                      # transformační funkce
    return n*n

result = map(squares, numbers); result
<map object at 0x000001AE79FA3460>   # iterátor,
list(result)                         # který nutno rozbalit
--> [1, 4, 9, 16]

Jiný příklad s funkcí lambda:

num1 = [4, 5, 6]        
num2 = [5, 6, 7]       # Iteráblů lze zadat více než jeden.

result = map(lambda x,y: x+y, num1, num2)      # iterátor
list(result)                        
--> [9, 11, 13]

Rozbalení stringů do entic použitím funkce list() a tuple():

tup = 'šach', 'mat', 'pat'                     # iterábl
test = list(map(tuple, tup))                   # iterátor
print(test)
--> [('š', 'a', 'c', 'h'), ('m', 'a', 't'), ('p', 'a', 't')]

Funkce map() je napsaná v jazyce C. Obsahuje interní smyčku for ... s menším nárokem na potřebu paměti než tatáž smyčka, psaná v Pythonu.


Funkce zip(iterables) přijímá dvě či více iterovatelných sekvencí (iteráblů) a vytváří interní objekt typu zip, v němž jsou zadané sekvence agregované do jediné sekvence entic iterátoru.

>>> izip = zip([1, 2, 3], "postel", range(6)); izip   
<zip object at 0x000001E753183380>;            # iterátor
>>> list(izip)                                   
[(1, 'p', 0), (2, 'o', 1), (3, 's', 2)]        # seznam

# Iterátor je prázdný, nutno jej znovu deklarovat
>>> izip = zip([1, 2, 3], "postel", range(1,5))
>>> set(izip)                                
{(3, 's', 2), (2, 'o', 1), (1, 'p', 0)}        # set
                                
# nebo sloučit deklaraci sekvence s deklarací iterátoru:
>>> tuple(zip([1, 2, 3], "postel", range(6)))      
((1, 'p', 0), (2, 'o', 1), (3, 's', 2))        # entice    
>>> dict(zip([1, 2, 3, 4], "postel"))        
{1: 'p', 2: 'o', 3: 's', 4: 't'}               # slovník

Počet entic ve výsledné sekvenci je dán počtem argumentů funkce zip. Délka výsledných entic je dána délkou nejkratšího argumentu. U slovníku mohou být pouze dva argumenty. Jeho enticemi jsou dvojice klíč:hodnota.

Ve verzi Python >= 3.10 existuje párový argument strict=True, který zkontroluje délku vstupních argumentů a v případě neshody hlásí chybu:

>>> set(zip([1, 2, 3, 4], "postel", strict=True))
ValueError: zip() argument 2 is longer than argument 1

Je nicméně možné vytvořit iterátor podle nejdelší vstupní sekvence a to pomocí funkce zip_longest z modulu itertools:

>>> from itertools import zip_longest
>>> numb = [1, 2, 3]
>>> lett = ['a', 'b', 'c']
>>> long = range(5)
>>> izip = zip_longest(numb, lett, long, fillvalue='#')
>>> list(izip)
[(1, 'a', 0), (2, 'b', 1), (3, 'c', 2), ('#', '#', 3), ('#', '#', 4)]
Souhrnná poznámka

Výstupem z funkce filter(), map(), zip() je iterátor a lze jím procházet (traverzovat) funkcí iter() nebo smyčkou for ... :

>>> izip = zip([1, 2, 3], "postel", range(6))
>>> next(izip)
(1, 'p', 0)
>>> for i in izip:
    print(i)
(2, 'o', 1)
(3, 's', 2)
>>> next(izip)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration                      # iterátor je prázdný
Prázdný iterátor zaplníme opětovným voláním proměnné izip.

Interní objekt iterátoru lze také rozbalit funkcí list(), tuple(), set() a dict.


5.9 Funkce functools.reduce

Funkce vyššího řádu reduce(fce, iterable[,initial]) vrací jedinou výslednou hodnotu z postupné aplikace zadané funkce pro dvě po sobě zadané hodnoty v sekvenci. Tuto funkci nutno importovat z modulu functools.

from functools import reduce
seqv = 1,2,3,4 
def do_sum(x, y):          # funkce pro dva argumenty
    return x + y
	
reduce(do_sum, seqv)                      --> 10
Případně s hodnotou pro parametr initial:
reduce(do_sum, seqv, 12)                  --> 22

Případně s funkcí lambda:
reduce(lambda x,y: x+y , range(1,5))      --> 10

Funkce reduce() zde postupně provádí součty (((1+2)+3)+4), eventuelně ((((12 (argument pro parametr initial) +1)+2)+3)+4).
Hodnota parametru 'initial' se při realizaci funkce reduce() zařadí na první pozici zpracovávané sekvence. Datové typy argumentů pro sekvenci i parametr 'initial' musejí být shodné.
Parametr initial je důležitý pro situaci, kdy by argument pro parametr iterable měl být prázdný. V tom případě by funkce reduce() bez argumentu pro initial skončíla sdělením TypeError.

Inteligentní náhradou funkce reduce je funkce sum(iterable[, start]), kde volitelný parametr 'start' je stejný jako parametr 'initial' u funkce reduce() - s tím rozdílem, že sum() neumí slučovat řetězce jako funkce reduce():

>>> from functools import reduce
>>> bar = '1','2','3','4'
>>> reduce(lambda x,y: x+y, bar, "sumace = ")
'sumace = 1234'

5.10 Anonymní funkce

Speciálním druhem funkcí jsou takzvané anonymní funkce, které používají společné označení lambda. Na rozdíl od ostatních funkcí se deklarují přímo v místě použití. Jejich syntaxe je velice prostá:

lambda parametr: výraz

Funkce lambda může mít libovolný počet parametrů ale jen jeden deklarovaný výraz (expression). Pro opakované použití je možné opatřit tuto funkci jménem:

>>> (lambda x, y: x * y)(2, 3)    # bezprostředně volané provedení funkce
6
>>> soucin = lambda x, y: x * y   # jméno umožňuje opakované použití 
>>> soucin(2, 3)
6

Anonymní funkci lze s výhodou použít jako argument pro jinou funkci (připadně ještě s jinou funkcí):

>>> numera = (2, 6, 8, 10, 11, 4, 12, 7, 13, 17, 0, 3, 21)   
>>> selekce = list(filter(lambda num: (num > 7), numera))
>>> print(selekce)
[8, 10, 11, 12, 13, 17, 21]

Ve funkci lambda lze rovněž použít variadický parametr **kwargs:

# parametry funkce jsou hodnoty (values) slovníku 'slov':
>>> slov = {'one':1, 'two':2, 'three':3}   
>>> mean_val = (lambda **slov: sum(slov.values()) / len(slov))
>>> mean_val(**slov)
2.0
# tato funkce pracuje prostřednictvím proměnné **kwargs se 
slovníkem (viz výše) i s enticí pojmenovaných (keyword) hodnot:                
>>> mean_val(one=1, two=2, three=3)        
2.0 

Poznámky:

Funkce list, tuple, set - jsou funkce pro vytvoření seznamu, entice a setu.
Funkce filter - viz odst. 5.8.


5.11 Glosář

podmíněný příkaz (conditional statement)
Příkaz, který řídí tok výpočtu v závislosti na nějaké podmínce. V Pythonu se pro podmíněné příkazy používají klíčová slova if, elif a   else.
podmínka (condition)
Booleovský výraz v podmíněném výrazu, který rozhoduje o tom, která větev se provede.
řetězená podmínka (chained conditional)
Podmíněné větvení s více než dvěma toky výpočtu. V Pythonu se řetězené podmínky píší pomocí příkazů if ... elif ... else.
větev (branch)
Jedna z možných cest výpočtu určená vyhodnocením podmínky.
iterace (iteration)
Opakované provádění sady programových příkazů
smyčka (loop)
Příkaz nebo skupina příkazů, které se provádějí opakovaně, dokud není splněna ukončující podmínka.
nekonečná smyčka (infinite loop)
Smyčka, ve které ukončující podmínka není nikdy splněna.
proměnná smyčky (loop variable)
Proměnná, jejíž hodnota se mění po každém provedení smyčky.
počítadlo (counter)
Proměnná pro počítání něčeho; obvykle nastavená s nulovou počáteční hodnotou a navyšovaná v těle smyčky.
tělo (body)
Příkazy uvnitř smyčky nebo funkce

5.12 Cvičení

Napište definice funkcí s invokacemi v zadaných doctestech. Všechny příklady zapisujte do samostatných souborů ve složce Kap-04.

Příklady s doctestem budou mít na konci skriptu tento kód:

if __name__ == '__main__':
    import doctest
    doctest.testmod()

Tyto příklady řešte až po prostudování textu v kap. 3.10.

  1. Napište funkci compare(a,b), která vrací 1 pro a>b, 0 pro a==b a -1 pro a<b.
    def compare (a, b):
        """
        >>> compare(5,4)
        1
        >>> compare(7,7)
        0
        >>> compare(2,3)
        -1
        """
        # Zde napište tělo funkce
    
  2. Napište funkci hypo, která vrací délku přepony pravoúhlého trojúhelníka pro zadané délky odvěsen.
    def hypo (a,b):
        """
        >>> hypo(3,4)
        5.0
        >>> hypo(12,5)
        13.0
        >>> hypo(7,24)
        25.0
        """
        # tělo funkce může mít pouhé 2 řádky
    
  3. Napište funkci slope(x1,y1,x2,y2), která vrací tangent úhlu (sklonu) přímky procházející body (x1,y1) a (x2,y2). Funkce musí vyhovět následujícím testům:
    def slope (x1,y1,x2,y2):
        """
        >>> slope(5,3,4,2)
        1.0
        >>> slope(1,2,3,2)
        0.0
        >>> slope(1,4,1,2)         
        kolmice k ose 'x'           Ošetřená výjimka!
        >>> slope(2,4,1,2)
        2.0
        """
        # Zde napište tělo funkce
    
  4. Napište funkci intercept(x1,y1,x2,y2), která vrací ypsilonovou pořadnici průsečíku přímky s osou y (pro x=0). Použijte rovnici přímky, procházející 2 body.

    def intercept (x1,y1,x2,y2):
        """
        >>> intercept(1,6,3,12)
        3.0
        >>> intercept(6,1,1,6)
        7.0
        >>> intercept(4,6,12,8)
        5.0
        """
        # Zde napište tělo funkce
    
  5. Napište funkci is_multiple(m,n), která vrátí True, je-li m násobkem n a False, není-li.
    def is_multiple( (m,n):
        """
        >>> is_multiple(12,3)
        True
        >>> is_multiple(12,4)
        True
        >>> is_multiple(12,5)
        False
        """
        # Zde napište tělo funkce
    
  6. Napište fci is_divisible(m,n), která přijme celá čísla jako argument a podle situace vytiskne True nebo False.

    def is_divisible( (m,n):
        """
        >>> is_divisible(20,4)
        True
        >>> is_divisible(21,8)
        False
        """
        # Zde napište tělo funkce
    

    Při řešení použijete podmínky if ... :, else: a vyberete si jeden z existujících způsobů dělení: normální a/b, celočíselné a//b nebo dělení se zbytkem (modulo) a%b.

  7. Napište tělo funkce fah2cel(t), která převede teplotu ve stupních Fahrenheitových na stupně Celsiovy a vrátí ji jako celé číslo. Použijete vestavěnou fci round(n). Informaci o této funkci získáte zadáním >>> round.__doc__ v konzole Pythonu.
    def fah2cel(t):
        """
        >>> fah2cel(212)
        100
        >>> fah2cel(32)
        0
        >>> fah2cel(-40)
        -40
        >>> fah2cel(37)
        3
        """
        # Zde napište tělo funkce
    
  8. Napište funkci cels2fah(t), která převede teplotu ve stupních Celsiových na stupně Fahrenheitovy.

cmment up next title end end