Dekorátory funkcí a metod nabízené knihovnou funcy

3. 10. 2023
Doba čtení: 43 minut

Sdílet

 Autor: Depositphotos
V již sedmé části seriálu, v němž se zabýváme funkcionálním programováním v Pythonu (ano, i to je do jisté míry možné) si ukážeme další mnohdy velmi užitečné dekorátory funkcí a metod nabízených knihovnou funcy.

Obsah

1. Dekorátory funkcí a metod nabízené knihovnou funcy

2. Dekorátor @decorator

3. Dekorátory zjednodušující reakce na výjimky

4. Dekorátor @silent

5. Příklady použití dekorátoru @silent

6. Zachycení výjimky, která je ve funkci vyhazována explicitně

7. Dekorátor @ignore

8. Příklady použití dekorátoru @ignore

9. Vrácení zvolené hodnoty z funkce, ve které byla výjimka zachycena

10. Rozdílné chování pro různé typy výjimek

11. Transformace výjimky na jiný typ výjimky (s odlišnou zprávou) dekorátorem @reraise

12. Ukázky použití dekorátoru @reraise

13. Reakce na větší množství typů výjimek

14. Využití informací z původní výjimky v nové výjimce

15. Pokusy o opakované spuštění funkce při vyhození výjimky – dekorátor @retry

16. Základní způsob použití dekorátoru @retry

17. Specifikace typu zachytávaných výjimek

18. Exponenciálně vzrůstající hodnota timeout mezi jednotlivými voláními funkce

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Dekorátory funkcí a metod nabízené knihovnou funcy

V již sedmé části seriálu, v němž se zabýváme funkcionálním programováním v Pythonu (ano, i to je do jisté míry možné) si ukážeme další mnohdy velmi užitečné dekorátory funkcí a metod nabízených knihovnou funcy. S některými dekorátory jsme se již v tomto seriálu setkali, takže si je jen ve stručnosti připomeňme. Posléze si popíšeme další (prozatím nezmíněné) dekorátory, jejichž využitím lze zdrojový kód zjednodušit popř. zpřehlednit:

Dekorátor Kapitola Stručný popis
@decorator 2 deklarace funkce, ze které se vytvoří dekorátor
@silent 4 ve funkci s dekorátorem se budou ignorovat všechny výjimky
@ignore 7 ve funkci s dekorátorem se budou ignorovat vybrané výjimky + možnost zadat návratovou hodnotu funkce
@reraise 11 transformace vyhozené výjimky na jiný typ výjimky (s odlišnou zprávou)
@retry 15 pokusy o opakované spuštění funkce při vyhození výjimky

2. Dekorátor @decorator

V první řadě se jedná o dekorátor nazvaný přímočaře @decorator. Jedná se o dekorátor, který je možné s výhodou využít pro deklaraci vlastního dekorátoru (vytvořeného z funkce), což může do značné míry zjednodušit zápis výsledného programu. Použití tohoto dekorátoru je ve skutečnosti až triviálně snadné, protože vlastně namísto původního wrapperu píšeme jen deklaraci „obalovací“ funkce. Zkusme si tedy převést funkci nazvanou wrapper na skutečný dekorátor, který vzápětí použijeme:

from funcy import decorator
 
 
@decorator
def wrapper1(function):
    print("-" * 40)
    function()
    print("-" * 40)
 
 
@wrapper1
def hello():
    print("Hello!")
 
 
hello()
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/de­corators1.py.

Pro úplnost se ještě podívejme na další příklad, který jsme si taktéž již ukázali v předchozích článcích. Jedná se o několikanásobnou aplikaci více dekorátorů, což znamená, že původní funkce je transformována do jiné funkce a ta je dále transformována do další funkce (atd.). Připomeňme si, že zdrojový kód tohoto příkladu (bez použití @decorator) vypadal následovně:

def wrapper1(function):
    def inner_function():
        print("-" * 40)
        function()
        print("-" * 40)
 
    return inner_function
 
 
def wrapper2(function):
    def inner_function():
        print("=" * 40)
        function()
        print("=" * 40)
 
    return inner_function
 
 
@wrapper1
@wrapper2
def hello():
    print("Hello!")
 
 
hello()

Opět se podívejme na způsob zjednodušení celé struktury tohoto příkladu do podoby založené na použití dekorátoru @decorator. Přepis je přímočarý:

from funcy import decorator
 
 
@decorator
def wrapper1(function):
    print("-" * 40)
    function()
    print("-" * 40)
 
 
@decorator
def wrapper2(function):
    print("=" * 40)
    function()
    print("=" * 40)
 
 
@wrapper1
@wrapper2
def hello():
    print("Hello!")
 
 
hello()
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/de­corators2.py.

Naposledy se podívejme na použití dekorátoru @decorator z balíčku funcy. Tentokráte upravíme příklad s dekorátorem, který dokáže změřit délku trvání nějaké operace v uživatelem specifikované funkci. Původní zápis (opět bez použití @decorator) vypadal následovně:

# Original code:
# https://pythonbasics.org/decorators/#Real-world-examples
 
 
import time
 
 
def measure_time(func):
    def wrapper(*arg):
        t = time.time()
        res = func(*arg)
        print("Function took " + str(time.time() - t) + " seconds to run")
        return res
 
    return wrapper
 
 
@measure_time
def tested_function(n):
    time.sleep(n)
 
 
tested_function(1)
tested_function(2)

Přepis do stručnější a čitelnější podoby s využitím dekorátoru @decorator:

from funcy import decorator
import time
 
 
@decorator
def measure_time(func):
    t = time.time()
    res = func()
    print("Function took " + str(time.time() - t) + " seconds to run")
    return res
 
 
@measure_time
def tested_function(n):
    print(f"Sleeping for {n} seconds")
    time.sleep(n)
 
 
tested_function(1)
tested_function(2)
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/me­asure_time.py.

3. Dekorátory zjednodušující reakce na výjimky

V knihovně Funcy nalezneme i několik dekorátorů, které dokážou modifikovat způsob zpracování výjimek, které vzniknou (přesněji řečeno jsou vyhozeny) ve funkci obalené dekorátorem. To vlastně znamená, že u takto označených funkcí je způsob zpracování výjimek naznačen i bez toho, abychom museli studovat kód takové funkce (což nám stejně nemusí pomoci, protože výjimky mohou být vyhozeny z interně volaných funkcí a metod). V navazujících kapitolách si ukážeme dekorátory určené pro zachycení a zahození všech výjimek, dále dekorátor, který dokáže zachytit pouze určené výjimky a můžeme v něm specifikovat i návratovou hodnotu z funkce (pokud výjimka nastala) a nakonec i dekorátor, který zachycené výjimky dokáže transformovat na jiný typ výjimky (takže například dokážeme všechny označené výjimky transformovat na výjimku typu InternalServerError, která je následně vrácena klientovi v HTTP odpovědi).

4. Dekorátor @silent

Prvním novým dekorátorem, se kterým se v dnešním článku seznámíme, je dekorátor nazvaný @silent. Použití tohoto dekorátoru je ve skutečnosti velmi jednoduché, protože slouží k zachycení a tichému zahození (odtud jeho jméno) jakékoli výjimky, která může v dekorované funkci nastat. Není tedy nutné ve funkci vytvářet řídicí strukturu try-except s prázdným blokem except.

Poznámka: jak prázdný blok except (který tedy bude pouze obsahovat klíčové slovo pass), tak i použití dekorátoru @silent, můžeme ve velkém množství případů považovat za antipattern; na druhou stranu však některé typy výjimek skutečně budeme chtít tímto způsobem odchytit a ihned poté zahodit (krátké skripty, funkce volané při ukončování aplikace, když vznikne jiná výjimka a proces je stejně v režimu „nezachytitelného pádu“, při zpracování proudu dat atd.). Ovšem je vhodné @silent používat jen ve skutečně oprávněných případech.

5. Příklady použití dekorátoru @silent

Dekorátor @silent se používá velmi snadno. Podívejme se například na následující funkci, která se pokusí vydělit své dva operandy a vrátit výsledek podílu. Tato funkce může (pochopitelně) vyhodit výjimku, pokud se dělí nulou:

def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledek je očekávatelný:

0.5
Traceback (most recent call last):
  File "silent_1.py", line 6, in <module>
    print(divide(1, 0))
  File "silent_1.py", line 2, in divide
    return a/b
ZeroDivisionError: division by zero
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent1.py.

Nyní funkci pro výpočet podílu „odekorujeme“ s využitím @silent:

from funcy import silent
 
@silent
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledky budou odlišné – při dělení nulou se vrátí hodnota None:

0.5
None
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent2.py.

Podobně se můžeme pokusit o deklaraci funkce, která vypíše obsah zvoleného souboru. I v tomto případě může dojít k vyhození výjimky (resp. dokonce několika typů výjimek):

def cat(filename):
    with open(filename) as fin:
        print(fin.read())
 
 
cat("silent_3.py")
cat("this_does_not_exists")

Druhý soubor neexistuje, takže dostaneme:

def cat(filename):
    with open(filename) as fin:
        print(fin.read())
 
 
cat("silent_3.py")
cat("this_does_not_exists")
 
Traceback (most recent call last):
  File "silent_3.py", line 7, in <module>
    cat("this_does_not_exists")
  File "silent_3.py", line 2, in cat
    with open(filename) as fin:
FileNotFoundError: [Errno 2] No such file or directory: 'this_does_not_exists'
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent3.py.

Pokud před hlavičku funkce přidáme dekorátor @silent, budou všechny výjimky zachyceny a funkce pouze vrátí hodnotu None (kterou stejně ignorujeme):

from funcy import silent
 
@silent
def cat(filename):
    with open(filename) as fin:
        print(fin.read())
 
 
cat("silent_3.py")
cat("this_does_not_exists")

Výsledky:

def cat(filename):
    with open(filename) as fin:
        print(fin.read())
 
 
cat("silent_3.py")
cat("this_does_not_exists")
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent4.py.

6. Zachycení výjimky, která je ve funkci vyhazována explicitně

Samozřejmě je možné, že výjimka je v uživatelské funkci vyhazována explicitně (tj. s využitím klíčového slova raise), což je ukázáno na tomto jednoduchém příkladu:

def raise_exception():
    raise Exception("foo")
 
 
raise_exception()

Po spuštění skriptu dostaneme podle očekávání tento výstup:

Traceback (most recent call last):
  File "silent_5.py", line 5, in <module>
    raise_exception()
  File "silent_5.py", line 2, in raise_exception
    raise Exception("foo")
Exception: foo
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent5.py.

I takovou výjimku je možné pomocí dekorátoru @silent zachytit (což se může hodit například při ladění programu atd. – není nutné zasahovat do kódu funkce):

from funcy import silent
 
@silent
def raise_exception():
    raise Exception("foo")
 
 
raise_exception()

Tento program po svém spuštění nic nevypíše, pouze se korektně ukončí.

Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent6.py.

A nakonec si, nyní již pouze pro úplnost, ukažme chování dekorátoru @silent u funkce, která volá jinou uživatelskou funkci, která vyhodí výjimku:

from funcy import silent
 
@silent
def call_function_to_raise_exception():
    raise_exception()
 
 
def raise_exception():
    raise Exception("foo")
 
 
call_function_to_raise_exception()

I tento program po svém spuštění nic nevypíše, pouze se korektně ukončí (s návratovým kódem nastaveným na nulu).

Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent7.py.

7. Dekorátor @ignore

Dekorátor @silent, který byl popsaný v předchozích dvou kapitolách, nelze žádným způsobem řídit ani upravit jeho chování. Mnohdy ovšem potřebujeme ignorovat jen určitý typ výjimky nebo typy výjimek. A současně mnohdy potřebujeme, aby při vzniku (a zachycení) výjimky funkce vrátila nějakou předem zadanou hodnotu, nikoli pouze výchozí hodnotu None (typicky nám to bude vadit při zpracování proudu dat). A právě v těchto případech lze s výhodou použít další dekorátor, který je nazvaný @ignore. Tomuto dekorátoru se předává jeden či dva parametry. Prvním parametrem jsou typy výjimek, které se mají zachytit (v tomto případě je typ shodný se jménem třídy s implementací výjimky) a parametrem druhým pak hodnota, která se z funkce vrátí v případě, že došlo k vyhození a zachycení výjimky (registrovaného typu).

8. Příklady použití dekorátoru @ignore

Podobně jako jsme si ukázali způsoby použití dekorátoru @silent si v této kapitole ukážeme, jak lze využít sofistikovanější dekorátor nazvaný @ignore.

Zachycení výjimky typu Exception a od ní odvozených výjimek:

from funcy import ignore
 
@ignore(errors=Exception)
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledky:

0.5
None
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore1.py.

Pokus o zachycení odlišné výjimky, než je ZeroDivisionError:

from funcy import ignore
 
@ignore(errors=IOError)
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

V tomto případě není výjimka zachycena a „probublá“ výše:

0.5
Traceback (most recent call last):
  File "ignore_2.py", line 9, in <module>
    print(divide(1, 0))
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 43, in wrapper
    return func(*args, **kwargs)
  File "ignore_2.py", line 5, in divide
    return a/b
ZeroDivisionError: division by zero
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore2.py.

Zachycení pouze jediné výjimky, která nás při výpočtu zajímá, tedy výjimky vyhození při dělení nulou:

from funcy import ignore
 
@ignore(errors=ZeroDivisionError)
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledky budou totožné s prvním příkladem z této kapitoly, tedy:

0.5
None
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore3.py.

9. Vrácení zvolené hodnoty z funkce, ve které byla výjimka zachycena

A konečně si ukažme velmi užitečnou vlastnost, tedy vrácení zvolené hodnoty v případě, že je výjimka zachycena:

from funcy import ignore
 
@ignore(errors=ZeroDivisionError, default=-1)
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

V tomto případě se při vzniku výjimky vrátí hodnota –1:

0.5
-1
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore4.py.

10. Rozdílné chování pro různé typy výjimek

Připomeňme si, že v parametru errors je možné dekorátoru @ignore předat seznam výjimek, které se mají zachytávat. Prozatím jsme namísto seznamu předávali pouze jeden typ výjimky, ovšem bez problémů je možné realizovat i následující funkci, v níž se zachytí výjimky typu ZeroDivisionError a/nebo TypeError (pokud předáme parametr typu, pro který není dělení realizovatelné):

from funcy import ignore
 
@ignore(errors=[ZeroDivisionError, TypeError], default=-1)
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))
print(divide(None, 1))

Výsledky:

0.5
-1
-1
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore6.py.

Pokud ovšem zachytáváme pouze ZeroDivisionError a nikoli již TypeError, bude výsledek podle očekávání odlišný:

0.5
-1
Traceback (most recent call last):
  File "ignore_5.py", line 10, in <module>
    print(divide(None, 1))
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 43, in wrapper
    return func(*args, **kwargs)
  File "ignore_5.py", line 5, in divide
    return a/b
TypeError: unsupported operand type(s) for /: 'NoneType' and 'int'
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore5.py.

Ve skutečnosti je možné na každou výjimku reagovat odlišně, tedy vrácením jiné hodnoty. To je potenciálně velmi užitečná vlastnost, takže se podívejme na realizaci takového programu. Je to vlastně triviální – použijeme dvojici dekorátorů:

from funcy import ignore
 
@ignore(errors=ZeroDivisionError, default=0)
@ignore(errors=TypeError, default=-1)
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))
print(divide(None, 1))

Výsledky by měly být následující:

0.5
0
-1
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore7.py.

11. Transformace výjimky na jiný typ výjimky (s odlišnou zprávou) dekorátorem @reraise

Dalším dekorátorem, který v knihovně Funcy nalezneme a který souvisí se zpracováním výjimek, je dekorátor nazvaný @reraise. Tento dekorátor dokáže zachytit výjimku či výjimky specifikovaného typu (typů) a namísto nich vyhodit jiný typ výjimky. Kde se ovšem tato funkcionalita uplatní? Například ve chvíli, kdy potřebujeme klientovi odeslat odpověď přes HTTP protokol s korektním stavem (200 OK, 404 Not Found atd.). Můžeme tedy zachytit ostatní typy výjimek (IOError atd.) a nechat si je přetransformovat právě do výjimky, která reprezentuje stav HTTP odpovědi – a to bez nutnosti psaní relativně složitého kódu založeného na sekvenci bloků try a except. Tuto funkcionalitu si ukážeme v navazujících dvou kapitolách.

Poznámka: ve skutečnosti bude původní výjimka součástí historie, což ostatně uvidíme na demonstračních příkladech.

12. Ukázky použití dekorátoru @reraise

Základní použití dekorátoru @reraise může vypadat následovně – libovolný typ výjimky (odvozené od třídy Exception) zachytíme a vyhodíme namísto ní výjimku typu MathError. Je tedy nutné dekorátoru @reraise předat parametry errors (jedna či více zachycovaných výjimek) a into (výjimka, která se má vyhodit):

from funcy import reraise
 
class MathError(Exception):
    def __init__(self, message):
        self.message = message
 
 
@reraise(errors=Exception, into=MathError("neděl nulou!"))
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Z výsledku je patrné, že se skutečně vyhodila nová výjimka a ta původní zůstala v historii:

0.5
Traceback (most recent call last):
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 84, in reraise
    yield
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "reraise_1.py", line 10, in divide
    return a/b
ZeroDivisionError: division by zero
 
The above exception was the direct cause of the following exception:
 
Traceback (most recent call last):
  File "reraise_1.py", line 14, in <module>
    print(divide(1, 0))
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "/usr/lib/python3.8/contextlib.py", line 131, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 88, in reraise
    raise into from e
__main__.MathError: neděl nulou!
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise1.py.

Pokud ovšem budeme zachytávat odlišný typ výjimky (zde konkrétně IOError), nebude dekorátor @reraise provádět žádnou viditelnou činnost:

from funcy import reraise
 
class MathException(Exception):
    def __init__(self, message):
        self.message = message
 
 
@reraise(errors=IOError, into=MathException("neděl nulou!"))
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledky:

0.5
Traceback (most recent call last):
  File "reraise_2.py", line 14, in <module>
    print(divide(1, 0))
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "reraise_2.py", line 10, in divide
    return a/b
ZeroDivisionError: division by zero
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise2.py.

13. Reakce na větší množství typů výjimek

Vzhledem k tomu, že dekorátoru @reraise můžeme v parametru errors předat nikoli pouze jednu, ale hned několik výjimek, lze při „dekoraci“ funkce použít i následující zápis:

from funcy import reraise
 
class MathException(Exception):
    def __init__(self, message):
        self.message = message
 
 
@reraise(errors=[ArithmeticError, IOError], into=MathException("neděl nulou!"))
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

V praxi dostaneme v tomto konkrétním případě shodné výsledky, jako v předchozím příkladu, protože výjimka IOError nebude nikdy vyhozena a tudíž ani zachycena:

0.5
Traceback (most recent call last):
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 84, in reraise
    yield
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "reraise_3.py", line 10, in divide
    return a/b
ZeroDivisionError: division by zero
 
The above exception was the direct cause of the following exception:
 
Traceback (most recent call last):
  File "reraise_3.py", line 14, in <module>
    print(divide(1, 0))
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "/usr/lib/python3.8/contextlib.py", line 131, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 88, in reraise
    raise into from e
__main__.MathException: neděl nulou!
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise3.py.

Ovšem současně to znamená, že sice lze použít dekorátor @reraise a současně nezachytit žádnou výjimku. Jak to bude vypadat v praxi?

from funcy import reraise
 
class MathException(Exception):
    def __init__(self, message):
        self.message = message
 
 
@reraise(errors=[], into=MathException("neděl nulou!"))
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledky:

0.5
Traceback (most recent call last):
  File "reraise_4.py", line 14, in <module>
    print(divide(1, 0))
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "reraise_4.py", line 10, in divide
    return a/b
ZeroDivisionError: division by zero
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise4.py.

14. Využití informací z původní výjimky v nové výjimce

Ještě si ukažme jeden příklad použití dekorátoru @reraise. Tentokrát se pokusíme o využití informací z původní výjimky. Příkladem může být zjištění (resp. přesněji řečeno přečtení) zprávy obsažené v původní výjimce (nemusí být ve všech typech výjimek!) a znovupoužití této zprávy při konstrukci nové výjimky, která se z funkce vyhodí. V tomto případě můžeme s výhodou použít anonymní funkci (lambda) tak, jak je to patrné ze zdrojového kódu následujícího demonstračního příkladu:

from funcy import reraise
 
class MathException(Exception):
    def __init__(self, message):
        self.message = message
 
 
@reraise(errors=Exception, into=lambda e: MathException("neděl nulou! " + str(e)))
def divide(a, b):
    return a/b
 
 
print(divide(1, 2))
print(divide(1, 0))

Výsledek bude v tomto případě vypadat následovně:

0.5
Traceback (most recent call last):
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 84, in reraise
    yield
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "reraise_5.py", line 10, in divide
    return a/b
ZeroDivisionError: division by zero
 
The above exception was the direct cause of the following exception:
 
Traceback (most recent call last):
  File "reraise_5.py", line 14, in <module>
    print(divide(1, 0))
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "/usr/lib/python3.8/contextlib.py", line 131, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 88, in reraise
    raise into from e
__main__.MathException: neděl nulou! division by zero
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise5.py.

15. Pokusy o opakované spuštění funkce při vyhození výjimky – dekorátor @retry

Posledním dekorátorem, s nímž se v dnešním článku seznámíme, je dekorátor nazvaný @retry. Tento dekorátor zajišťuje, že se nějaká funkce s tímto dekorátorem bude volat opakovaně až do chvíle, kdy buď skončí úspěšně (tedy bez vyhození výjimky) nebo dokud počet opakování nepřesáhne programátorem zadanou hranici (například deset pokusů). Dokonce je možné specifikovat i prodlevu mezi jednotlivými opakováními, což je výhodné například ve chvíli, kdy došlo k výpadku sítě a tedy nemá smysl se pokoušet o připojení bez větší časové prodlevy. Relativně snadno si lze vynutit exponenciálně se zvyšující hodnotu prodlevy, což ostatně uvidíme na demonstračním příkladu.

16. Základní způsob použití dekorátoru @retry

Základní způsob použití dekorátoru @retry je vlastně velmi jednoduchý, protože nám postačuje pouze zadat hraniční počet opakování funkce. V případě, že volaná funkce bude stále vyhazovat výjimku, pokusy o její opakované spuštění po dosažení určeného počtu budou ukončeny a výjimka bude skutečně vyhozena:

from funcy import retry
 
 
@retry(3)
def call_function_to_raise_exception():
    print("Trying to call problematic code...")
    raise_exception()
 
 
def raise_exception():
    raise Exception("foo")
 
 
while True:
    call_function_to_raise_exception()

Výsledky ukazují celkem čtyři pokusy o volání funkce:

Trying to call problematic code...
Trying to call problematic code...
Trying to call problematic code...
Traceback (most recent call last):
  File "retry_1.py", line 15, in <module>
    call_function_to_raise_exception()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 47, in wrapper
    return deco(call, *dargs, **dkwargs)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 99, in retry
    return call()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 68, in __call__
    return self._func(*self._args, **self._kwargs)
  File "retry_1.py", line 7, in call_function_to_raise_exception
    raise_exception()
  File "retry_1.py", line 11, in raise_exception
    raise Exception("foo")
Exception: foo
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry1.py.

Alternativně můžeme zadat prodlevu mezi jednotlivými voláními funkce, která je specifikovaná v sekundách:

from funcy import retry
 
 
@retry(3, timeout=1)
def call_function_to_raise_exception():
    print("Trying to call problematic code...")
    raise_exception()
 
 
def raise_exception():
    raise Exception("foo")
 
 
while True:
    call_function_to_raise_exception()

V našem konkrétním případě bude výsledek totožný, ovšem dosáhneme ho až za zhruba čtyři sekundy:

Trying to call problematic code...
Trying to call problematic code...
Trying to call problematic code...
Traceback (most recent call last):
  File "retry_2.py", line 15, in <module>
    call_function_to_raise_exception()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 47, in wrapper
    return deco(call, *dargs, **dkwargs)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 99, in retry
    return call()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 68, in __call__
    return self._func(*self._args, **self._kwargs)
  File "retry_2.py", line 7, in call_function_to_raise_exception
    raise_exception()
  File "retry_2.py", line 11, in raise_exception
    raise Exception("foo")
Exception: foo
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry2.py.

17. Specifikace typu zachytávaných výjimek

Podobně jako u některých výše zmíněných dekorátorů, i u dekorátoru @retry je možné specifikovat, které výjimky budou zpracovány a které přímo povedou k ukončení funkce. Například následující kód skončí ihned po prvním volání funkce, protože zpracováváme pouze výjimku typu IOError a nikoli Exception:

from funcy import retry
 
 
@retry(3, timeout=1, errors=IOError)
def call_function_to_raise_exception():
    print("Trying to call problematic code...")
    raise_exception()
 
 
def raise_exception():
    raise Exception("foo")
 
 
while True:
    call_function_to_raise_exception()

Z výsledků je patrné, že nyní skutečně nedošlo ke snaze o opětovné volání funkce:

Trying to call problematic code...
Traceback (most recent call last):
  File "retry_3.py", line 15, in <module>
    call_function_to_raise_exception()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 47, in wrapper
    return deco(call, *dargs, **dkwargs)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 99, in retry
    return call()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 68, in __call__
    return self._func(*self._args, **self._kwargs)
  File "retry_3.py", line 7, in call_function_to_raise_exception
    raise_exception()
  File "retry_3.py", line 11, in raise_exception
    raise Exception("foo")
Exception: foo
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry3.py.

Jinak tomu bude (logicky) tehdy, pokud budeme explicitně zpracovávat výjimku typu Exception:

from funcy import retry
 
 
@retry(3, timeout=1, errors=Exception)
def call_function_to_raise_exception():
    print("Trying to call problematic code...")
    raise_exception()
 
 
def raise_exception():
    raise Exception("foo")
 
 
while True:
    call_function_to_raise_exception()

Nyní je již vše v pořádku:

Trying to call problematic code...
Trying to call problematic code...
Trying to call problematic code...
Traceback (most recent call last):
  File "retry_4.py", line 15, in <module>
    call_function_to_raise_exception()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 47, in wrapper
    return deco(call, *dargs, **dkwargs)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 99, in retry
    return call()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 68, in __call__
    return self._func(*self._args, **self._kwargs)
  File "retry_4.py", line 7, in call_function_to_raise_exception
    raise_exception()
  File "retry_4.py", line 11, in raise_exception
    raise Exception("foo")
Exception: foo
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry4.py.

18. Exponenciálně vzrůstající hodnota timeout mezi jednotlivými voláními funkce

V dnešním posledním demonstračním příkladu je ukázáno, jak lze zajistit, aby se prodleva mezi jednotlivými voláními funkce exponenciálně zvyšovala. To je častý požadavek, zejména pokud se čeká na inicializaci nějaké služby, na obnovení činnosti sítě atd. – to poslední, čeho chceme dosáhnout, je přetížení prostředku (například oné sítě) v důsledku častých dotazů. A právě neustále se zvyšující prodleva mezi požadavky může být vhodným řešením. V praxi vypadá následovně:

ict ve školství 24

from funcy import retry
 
 
@retry(4, timeout=lambda delay: 2 ** delay, errors=Exception)
def call_function_to_raise_exception():
    print("Trying to call problematic code...")
    raise_exception()
 
 
def raise_exception():
    raise Exception("foo")
 
 
while True:
    call_function_to_raise_exception()

Po spuštění tohoto příkladu je patrné, že se funkce zavolá celkem pětkrát a teprve poté se výjimka skutečně vyhodí (co již vidět není, jsou časy jednotlivých volání – přidejte si proto do kódu vámi oblíbenou logovací knihovnu, aby se časy zobrazily):

Trying to call problematic code...
Trying to call problematic code...
Trying to call problematic code...
Trying to call problematic code...
Traceback (most recent call last):
  File "retry_5.py", line 15, in <module>
    call_function_to_raise_exception()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 47, in wrapper
    return deco(call, *dargs, **dkwargs)
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/flow.py", line 99, in retry
    return call()
  File "/home/ptisnovs/.local/lib/python3.8/site-packages/funcy/decorators.py", line 68, in __call__
    return self._func(*self._args, **self._kwargs)
  File "retry_5.py", line 7, in call_function_to_raise_exception
    raise_exception()
  File "retry_5.py", line 11, in raise_exception
    raise Exception("foo")
Exception: foo
 
shell returned 1
Poznámka: Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry5.py.

19. Repositář s demonstračními příklady

Všechny Pythonovské skripty, které jsme si prozatím v tomto seriálu ukázali, naleznete na adrese https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady (pro jejich spuštění je nutné mít nainstalovánu některou z podporovaných verzí Pythonu 3, a pro dnešní příklady i výše zmíněnou knihovnu funcy):

# Příklad Stručný popis Adresa
1 binary_operator.py ukázka funkce vyššího řádu, která jako parametr akceptuje jinou funkci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/binary_operator.py
2 get_operator1.py ukázka funkce vyššího řádu, která vrací jinou funkci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/get_operator1.py
3 get_operator2.py ukázka funkce vyššího řádu, která vrací jinou funkci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/get_operator2.py
4 standard_operators.py použití standardních operátorů přepsaných do formy funkce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/standard_operators.py
       
5 binary_operator_types.py varianta příkladu binary_operator.py s plnými typovými deklaracemi https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/binary_operator_types.py
6 get_operator_types.py varianta příkladu get_operator2.py s plnými typovými deklaracemi https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/get_operator_types.py
       
7 map1.py příklad použití funkce map: výpočet délky všech slov v textu https://github.com/tisnik/most-popular-python-libs/blob/master/functools/map1.py
8 map2.py příklad použití funkce map: výpočet absolutní hodnoty všech členů posloupnosti https://github.com/tisnik/most-popular-python-libs/blob/master/functools/map2.py
9 map3.py příklad použití funkce map: aplikace vlastní pojmenované funkce https://github.com/tisnik/most-popular-python-libs/blob/master/functools/map3.py
10 map4.py příklad použití funkce map: aplikace vlastního lambda výrazu https://github.com/tisnik/most-popular-python-libs/blob/master/functools/map4.py
       
11 map_list_comprehension1.py přepis skriptu map1.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/map_list_comprehension.py
12 map_list_comprehension2.py přepis skriptu map2.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/map_list_comprehension.py
13 map_list_comprehension3.py přepis skriptu map3.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/map_list_comprehension.py
14 map_list_comprehension4.py přepis skriptu map4.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/map_list_comprehension.py
       
15 filter1.py filtrace dat na základě délky řetězce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/filter1.py
16 filter2.py filtrace numerických dat podle toho, zda se jedná o sudá či lichá čísla https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/filter2.py
17 filter3.py přepis předchozího příkladu s využitím lambda výrazu https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/filter3.py
       
18 filter_list_comprehension1.py přepis skriptu filter_list_comprehension1.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/filter_list_comprehensi­on1.py
19 filter_list_comprehension2.py přepis skriptu filter_list_comprehension2.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/filter_list_comprehensi­on2.py
20 filter_list_comprehension3.py přepis skriptu filter_list_comprehension3.py tak, aby se použila generátorová notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/filter_list_comprehensi­on3.py
       
21 reduce1.py výpočet faktoriálu s využitím funkce vyššího řádu reduce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/reduce1.py
22 reduce2.py přepis předchozího příkladu s využitím lambda výrazu https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/reduce2.py
23 reduce3.py tisk tabulky faktoriálů https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/reduce3.py
24 reduce4.py přepis předchozího příkladu s využitím lambda výrazu https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/reduce4.py
25 reduce5.py přepis předchozího příkladu s využitím generátorové notace https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/reduce5.py
       
26 return_function.py funkce jako návratová hodnota jiné funkce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/return_function.py
27 closure_adder1.py příklad použití uzávěru – konstrukce funkce typu adder https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/closure_adder1.py
28 counter_closure1.py nekorektní implementace čítače s využitím uzávěrů https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/counter_closure1.py
29 counter_closure2.py přístup k nelokálnímu symbolu (Python 2.x i Python 3.x) https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/counter_closure2.py
30 counter_closure3.py přístup k nelokálnímu symbolu (pouze Python 3.x) https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/counter_closure3.py
       
31 access_nonlocal_symbol.py přístup k nelokálnímu symbolu v uzávěru https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/access_nonlocal_symbol.py
32 functions_and_closures.py funkce a uzávěry (umělý příklad) https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/functions_and_closures.py
       
33 partial1.py funkce doubler odvozená (redukcí) z univerzálnější funkce mul https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial1.py
34 partial2.py transformace funkce se třemi parametry s využitím partial (nekorektní řešení) https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial2.py
35 partial3.py transformace funkce se třemi parametry s využitím partial (korektní řešení) https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial3.py
36 partial4.py transformace funkce s dosazením většího množství parametrů s využitím partial https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial4.py
37 partial5.py několikanásobná transformace původní funkce na několik nových funkcí https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial5.py
38 partial6.py postupná transformace již ztransformovaných funkcí https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial6.py
39 partial7.py typ originální funkce i funkcí získaných s využitím transformace pomocí partial https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial7.py
40 partial8.py jméno funkce, poziční argumenty funkce a pojmenované argumenty funkce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial8.py
41 partial9.py transformace reduce a pojmenované argumenty původní funkce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial9.py
42 partial_A.py získání informací o redukované funkci s pojmenovanými argumenty https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial_A.py
       
43 partial_method1.py třída s metodou s parametry https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial_method1.py
44 partial_method2.py třída s metodou s parametry – vliv volání setteru na stav objektu https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial_method2.py
45 partial_method3.py metody enable a disable vzniklé transformací set_enabled https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial_method3.py
46 partial_method4.py další příklad použití funkce partialmethod – doplnění většího množství parametrů při transformaci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/partial_method4.py
       
47 lru_cache1.py klasický výpočet Fibonacciho posloupnosti rekurzivní funkcí https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/lru_cache1.py
48 lru_cache2.py LRU cache pro výsledky Fibonacciho posloupnosti pro nejčastěji použité vstupy https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/lru_cache2.py
49 lru_cache3.py přečtení informací o využití LRU cache https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/lru_cache3.py
50 lru_cache4.py programové vymazání LRU cache https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/lru_cache4.py
       
51 cached_property1.py použití standardního dekorátoru @property https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/cached_property1.py
52 cached_property2.py použití dekorátoru @cached_property https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/cached_property2.py
53 cached_property3.py úprava předchozího příkladu tak, aby se využila LRU cache https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/cached_property3.py
       
54 total_ordering.py příklad implementace relačních operátorů založených na dekorátoru @total_ordering https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/total_ordering.py
       
55 decorators2.py příklad použití jednoho dekorátoru aplikovaného na funkci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/decorators2.py
56 decorators3.py příklad použití dvou dekorátorů aplikovaných na funkci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/decorators3.py
57 measure_time.py praktické použití dekorátoru – měření doby trvání funkce označené dekorátorem https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/measure_time.py
       
58 decorators4.py atributy transformované funkce vytvořené s využitím dekorátoru https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/decorators4.py
59 decorators5.py využití dekorátoru @wraps pro automatickou úpravu atributů „obalované“ funkce https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/decorators5.py
60 decorators6.py dekorátor @wraps a několikanásobná aplikace dekorátorů na „obalovanou“ funkci https://github.com/tisnik/most-popular-python-libs/blob/master/functool­s/decorators6.py
       
61 decorators1.py zkrácená deklarace dekorátoru s využitím dekorátoru @decorator https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/de­corators1.py
62 decorators2.py několikanásobná aplikace dekorátorů, dekorátor je vytvořen přes @decorator https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/de­corators2.py
63 measure_time.py měření doby trvání funkce označené dekorátorem, dekorátor je vytvořen přes @decorator https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/measure_time.py
       
64 curry1.py curryfikace funkce se dvěma parametry https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/curry1.py
65 curry2.py curryfikace funkce se třemi parametry https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/curry2.py
66 rcurry1.py curryfikace funkce div funkcí curry https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/rcurry1.py
67 rcurry2.py curryfikace funkce div funkcí rcurry https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/rcurry2.py
68 rcurry3.py curryfikace funkce n_pow funkcí rcurry https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/rcurry3.py
       
69 autocurry.py curryfikace funkce n_pow funkcí autocurry (s odlišným výsledkem) https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/autocurry.py
       
70 compose1.py kompozice funkcí s využitím funkce vyššího řádu compose https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/compose1.py
71 compose2.py kompozice funkcí s využitím funkce vyššího řádu compose https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/compose2.py
72 compose3.py kompozice funkcí s využitím funkce vyššího řádu compose https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/compose3.py
73 rcompose1.py kompozice funkcí s využitím funkce vyššího řádu rcompose https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/rcompose1.py
74 rcompose2.py kompozice funkcí s využitím funkce vyššího řádu rcompose https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/rcompose2.py
75 rcompose3.py kompozice funkcí s využitím funkce vyššího řádu rcompose https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/rcompose3.py
       
76 filter1.py použití standardní funkce filter (opakování) https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter1.py
77 filter2.py použití funkce filter z knihovny funcy https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter2.py
78 filter3.py funkce vyššího řádu lfilter https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter3.py
79 filter4.py předání lambda výrazu do funkcí vyššího řádu filter a lfilter https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter4.py
80 filter5.py regulární výraz ve funkci predikátu https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter5.py
81 filter6.py množina ve funkci predikátu https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter6.py
82 filter7.py množina ve funkci predikátu https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/filter7.py
       
83 remove1.py použití funkce vyššího řádu remove https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/remove1.py
84 remove2.py použití funkce vyššího řádu lremove https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/remove2.py
85 remove3.py kombinace regulárního výrazu a funkce vyššího řádku lremove https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/remove3.py
86 remove4.py kombinace množin a funkce vyššího řádku lremove https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/remove4.py
       
87 map1.py použití standardní funkce map (opakování) https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/map1.py
88 map2.py použití funkce map z knihovny funcy https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/map2.py
89 map3.py funkce vyššího řádu lmap https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/map3.py
90 map4.py použití regulárního výrazu namísto transformační funkce v lmap https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/map4.py
91 map5.py použití regulárního výrazu namísto transformační funkce v lmap https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/map5.py
       
92 distinct1.py získání unikátních prvků ze vstupní sekvence funkcí distinct https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/distinct1.py
93 distinct2.py získání unikátních prvků ze vstupní sekvence funkcí distinct https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/distinct2.py
       
94 take_while1.py filtrace prvků sekvence funkcí takewhile https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ta­ke_while1.py
95 take_while2.py filtrace prvků sekvence funkcí takewhile https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ta­ke_while2.py
96 drop_while1.py filtrace prvků sekvence funkcí dropwhile https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/drop_whi­le1.py
97 drop_while2.py filtrace prvků sekvence funkcí dropwhile https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/drop_whi­le2.py
       
98 split1.py rozdělení sekvence na prvky odpovídající predikátu a na prvky, které predikátu neodpovídají https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/split1.py
99 split2.py rozdělení sekvence na prvky odpovídající predikátu a na prvky, které predikátu neodpovídají https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/split2.py
100 split3.py rozdělení sekvence na prvky odpovídající predikátu a na prvky, které predikátu neodpovídají https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/split3.py
101 split4.py rozdělení sekvence na prvky odpovídající predikátu a na prvky, které predikátu neodpovídají https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/split4.py
       
102 split_by1.py kombinace funkcí vyššího řádu takewhile a dropwhile https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/split_by1.py
103 split_by2.py kombinace funkcí vyššího řádu takewhile a dropwhile https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/split_by2.py
       
104 partition_by.py rozdělení sekvence na větší množství sekvencí funkcí partition_by https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/partition_by.py
       
105 count1.py nekonečná sekvence čítačů s hodnotami 0, 1, 2, … https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count1.py
106 count2.py získání prvních dvaceti hodnot čítačů z původně nekonečné sekvence https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count2.py
107 count3.py kombinace konečné a nekonečné sekvence s využitím standardní funkce zip https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count3.py
108 count4.py kombinace dvou nekonečných sekvencí s využitím standardní funkce zip https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count4.py
109 count5.py specifikace počáteční hodnoty nekonečné sekvence https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count5.py
110 count6.py specifikace počáteční hodnoty a kroku nekonečné sekvence https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count6.py
111 count7.py specifikace záporného kroku nekonečné sekvence https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count7.py
112 count8.py neceločíselný krok i hodnota prvního prvku https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/count8.py
       
112 cycle1.py opakující se nekonečná sekvence dvou hodnot https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/cycle1.py
113 cycle2.py opakující se nekonečná sekvence dvou hodnot https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/cycle2.py
114 cycle3.py kombinace dvou opakujících se nekonečných sekvencí https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/cycle3.py
115 cycle4.py sekvence obsahující označení všech hracích karet https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/cycle4.py
       
116 group_by1.py rozdělení numerických hodnot do deseti skupin https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/group_by1.py
117 group_by2.py rozdělení slov do skupin podle délky https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/group_by2.py
118 group_by3.py rozdělení slov do skupin podle délky, výsledek je seřazen https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/group_by3.py
       
119 iterate1.py sekvence tvořená aritmetickou řadou https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/iterate1.py
120 iterate2.py sekvence tvořená aritmetickou řadou https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/iterate2.py
121 iterate3.py sekvence s celočíselnými mocninami dvojky https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/iterate3.py
122 iterate4.py vygenerování Fibonacciho posloupnosti (neúplné řešení) https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/iterate4.py
123 iterate5.py vygenerování Fibonacciho posloupnosti (úplné řešení) https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/iterate5.py
       
124 repeat1.py vygenerování konečné sekvence s deseti prvky https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/repeat1.py
125 repeat2.py vygenerování konečné sekvence s deseti prvky, převod sekvence na seznam https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/repeat2.py
126 repeat3.py vygenerování nekonečné sekvence bez její materializace https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/repeat3.py
127 repeat4.py vygenerování nekonečné sekvence s pokusem o její materializaci https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/repeat4.py
128 repeat5.py získání několika prvků z původně nekonečné sekvence https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/repeat5.py
129 repeat6.py kombinace konečné a nekonečné sekvence s využitím standardní funkce zip https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/repeat6.py
       
130 interleave.py kombinace prvků dvou nekonečných sekvencí https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/interleave.py
       
131 silent1.py výpočet podílu, funkce může vyhodit výjimku https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent1.py
132 silent2.py výpočet podílu, výjimka je zachycena přes @silent https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent2.py
133 silent3.py čtení ze souboru, funkce může vyhodit výjimku https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent3.py
134 silent4.py čtení ze souboru, výjimka je zachycena přes @silent https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent4.py
135 silent5.py explicitní vyhození výjimky s využitím raise https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent5.py
136 silent6.py explicitní vyhození výjimky s využitím raise, zachycení přes @silent https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent6.py
137 silent7.py explicitní vyhození výjimky ve volané funkci, opět zachycení přes @silent https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/silent7.py
       
138 ignore1.py zachycení výjimky typu Exception a od ní odvozených výjimek https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore1.py
139 ignore2.py pokus o zachycení odlišné výjimky, než je ZeroDivisionError https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore2.py
140 ignore3.py zachycení pouze jediné výjimky, která nás při výpočtu zajímá, tedy výjimky vyhození při dělení nulou https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore3.py
141 ignore4.py vrácení zvolené hodnoty v případě, že je výjimka zachycena https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore4.py
142 ignore5.py zachycení výjimky ZeroDivisionError, ignorace výjimky TypeError https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore5.py
143 ignore6.py zachycení výjimky ZeroDivisionError a/nebo TypeError https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore6.py
144 ignore7.py vrácení odlišných hodnot pro různé typy výjimek https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/ignore7.py
       
145 reraise1.py základní způsob použití dekorátoru @reraise https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise1.py
146 reraise2.py reakce na odlišný typ výjimky https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise2.py
147 reraise3.py reakce na větší množství typů výjimek https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise3.py
148 reraise4.py použití @reraise, ovšem s prázdným parametrem errors https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise4.py
149 reraise5.py využití informací z původní výjimky v nové výjimce https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/reraise5.py
       
150 retry1.py základní způsob použití dekorátoru @retry https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry1.py
151 retry2.py specifikace parametru timeout https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry2.py
152 retry3.py specifikace typu zachytávaných výjimek https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry3.py
153 retry4.py specifikace typu zachytávaných výjimek https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry4.py
154 retry5.py exponenciálně vzrůstající hodnota timeout https://github.com/tisnik/most-popular-python-libs/blob/master/funcy/retry5.py

20. Odkazy na Internetu

  1. functools — Higher-order functions and operations on callable objects
    https://docs.python.org/3/li­brary/functools.html
  2. Functional Programming HOWTO
    https://docs.python.org/3/how­to/functional.html
  3. Functional Programming in Python: When and How to Use It
    https://realpython.com/python-functional-programming/
  4. Functional Programming With Python
    https://realpython.com/learning-paths/functional-programming/
  5. Awesome Functional Python
    https://github.com/sfermigier/awesome-functional-python
  6. Currying
    https://en.wikipedia.org/wi­ki/Currying
  7. Currying in Python – A Beginner’s Introduction
    https://www.askpython.com/pyt­hon/examples/currying-in-python
  8. Fundamental Concepts in Programming Languages
    https://en.wikipedia.org/wi­ki/Fundamental_Concepts_in_Pro­gramming_Languages
  9. When should I use function currying?
    https://stackoverflow.com/qu­estions/24881604/when-should-i-use-function-currying
  10. Toolz
    https://github.com/pytool­z/toolz/tree/master
  11. Coconut: funkcionální jazyk s pattern matchingem kompatibilní s Pythonem
    https://www.root.cz/clanky/coconut-funkcionalni-jazyk-s-pattern-matchingem-kompatibilni-s-pythonem/
  12. A HITCHHIKER'S GUIDE TO functools
    https://ep2021.europython­.eu/media/conference/slides/a-hitchhikers-guide-to-functools.pdf
  13. Coconut aneb funkcionální nadstavba nad Pythonem (2.část)
    https://www.root.cz/clanky/coconut-aneb-funkcionalni-nadstavba-nad-pythonem-2-cast/
  14. Knihovny pro zpracování posloupností (sekvencí) v Pythonu
    https://www.root.cz/clanky/knihovny-pro-zpracovani-posloupnosti-sekvenci-v-pythonu/
  15. clj – repositář s knihovnou
    https://github.com/bfontaine/clj
  16. clj 0.1.0 – stránka na PyPi
    https://pypi.python.org/py­pi/clj/0.1.0
  17. Clojure aneb jazyk umožňující tvorbu bezpečných vícevláknových aplikací pro JVM (4.část – kolekce, sekvence a lazy sekvence)
    https://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  18. Clojure a bezpečné aplikace pro JVM: sekvence, lazy sekvence a paralelní programy
    https://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  19. Coconut: Simple, elegant, Pythonic functional programming
    http://coconut-lang.org/
  20. coconut (Python package index)
    https://pypi.python.org/pypi/coconut/
  21. Coconut Tutorial
    http://coconut.readthedoc­s.io/en/master/HELP.html
  22. Coconut FAQ
    http://coconut.readthedoc­s.io/en/master/FAQ.html
  23. Coconut Documentation
    http://coconut.readthedoc­s.io/en/master/DOCS.html
  24. Coconut na Redditu
    https://www.reddit.com/r/Pyt­hon/comments/4owzu7/coconut_fun­ctional_programming_in_pyt­hon/
  25. Repositář na GitHubu
    https://github.com/evhub/coconut
  26. Object-Oriented Programming — The Trillion Dollar Disaster
    https://betterprogramming.pub/object-oriented-programming-the-trillion-dollar-disaster-92a4b666c7c7
  27. Goodbye, Object Oriented Programming
    https://cscalfani.medium.com/goodbye-object-oriented-programming-a59cda4c0e53
  28. So You Want to be a Functional Programmer (Part 1)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-1–1f15e387e536
  29. So You Want to be a Functional Programmer (Part 2)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-2–7005682cec4a
  30. So You Want to be a Functional Programmer (Part 3)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-3–1b0fd14eb1a7
  31. So You Want to be a Functional Programmer (Part 4)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-4–18fbe3ea9e49
  32. So You Want to be a Functional Programmer (Part 5)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-5-c70adc9cf56a
  33. So You Want to be a Functional Programmer (Part 6)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-6-db502830403
  34. Why Programmers Need Limits
    https://cscalfani.medium.com/why-programmers-need-limits-3d96e1a0a6db
  35. Infographic showing code complexity vs developer experience
    https://twitter.com/rossi­pedia/status/1580639227313676288
  36. Python's reduce(): From Functional to Pythonic Style
    https://realpython.com/python-reduce-function/
  37. What is the problem with reduce()?
    https://stackoverflow.com/qu­estions/181543/what-is-the-problem-with-reduce
  38. The fate of reduce() in Python 3000
    https://www.artima.com/we­blogs/viewpost.jsp?thread=98196
  39. Reading 16: Map, Filter, Reduce
    http://web.mit.edu/6.031/www/sp22/clas­ses/16-map-filter-reduce/
  40. Currying
    https://sw-samuraj.cz/2011/02/currying/
  41. Používání funkcí v F#
    https://docs.microsoft.com/cs-cz/dotnet/fsharp/tutorials/using-functions
  42. Funkce vyššího řádu
    http://naucte-se.haskell.cz/funkce-vyssiho-radu
  43. Currying (Wikipedia)
    https://en.wikipedia.org/wi­ki/Currying
  44. Currying (Haskell wiki)
    https://wiki.haskell.org/Currying
  45. Haskell Curry
    https://en.wikipedia.org/wi­ki/Haskell_Curry
  46. Moses Schönfinkel
    https://en.wikipedia.org/wi­ki/Moses_Sch%C3%B6nfinkel
  47. ML – funkcionální jazyk s revolučním typovým systémem
    https://www.root.cz/clanky/ml-funkcionalni-jazyk-s-revolucnim-typovym-systemem/
  48. Funkce a typový systém programovacího jazyka ML
    https://www.root.cz/clanky/funkce-a-typovy-system-programovaciho-jazyka-ml/
  49. Curryfikace (currying), výjimky a vlastní operátory v jazyku ML
    https://www.root.cz/clanky/curryfikace-currying-vyjimky-a-vlastni-operatory-v-jazyku-ml/
  50. Primer on Python Decorators
    https://realpython.com/primer-on-python-decorators/
  51. Python Decorators
    https://www.programiz.com/python-programming/decorator
  52. PythonDecorators (Python Wiki)
    https://wiki.python.org/mo­in/PythonDecorators
  53. Funcy na GitHubu
    https://github.com/suor/funcy/
  54. Welcome to funcy documentation!
    https://funcy.readthedocs­.io/en/stable/
  55. Funcy cheatsheet
    https://funcy.readthedocs­.io/en/stable/cheatsheet.html
  56. PyToolz API Documentation
    https://toolz.readthedocs­.io/en/latest/index.html
  57. Toolz (PyToolz) na GitHubu
    https://github.com/pytoolz/toolz
  58. Fn.py: enjoy FP in Python
    https://github.com/kachayev/fn.py
  59. Funcy na PyPi
    https://pypi.org/project/funcy/
  60. Underscore aneb další knihovna pro funkcionální programování v JavaScriptu
    https://www.root.cz/clanky/underscore-aneb-dalsi-knihovna-pro-funkcionalni-programovani-v-javascriptu/
  61. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  62. Awesome functional Python
    https://github.com/sfermigier/awesome-functional-python
  63. lispy
    https://pypi.org/project/lispy/
  64. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  65. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  66. Hy na GitHubu
    https://github.com/hylang/hy
  67. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  68. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  69. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  70. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  71. Getting Hy on Python
    https://lwn.net/Articles/596626/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.