Python – Výjimky

Python – Výjimky

Výjimka je mechanismus pro zachycení a ošetření chybových a neočekávaných stavů. Výjimka umožňuje zachytit, přeskočit, nebo jinak ošetřit nastalý chybový stav až za samotným algoritmem, který chybu vyvolal a zvýšit tím čitelnost programového kódu.

Takže pojďme se podívat na výjimky v Pythonu.

Jak to vypadá, když program v Pythonu vyvolá výjimku

>>> x = 5/0
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Kdy čekat výjimku?

Kdykoli, kdy se může něco pokazit.
Takže při práci se soubory, databází, uživatelským vstupem, se sítí atd.

Zachycení konkrétní výjimky

try:
    # Sledovaný kód.
except (ZeroDivisionError):
    # Pokud nastane výjimka dělení nulou,
    # provede se kód v této části.
    # V případě, že chcete zachytávat více výjimek, stačí přidat
    # jejich jména oddělená čárkou.
else:
    # Tento kód se provede,
    # pokud nebude v bloku try vyvolána výjimka.
    # Tento statement je nepovinný.
finally:
    # Kód v této části bude spuštěn vždy při opuštění bloku try,
    # bez ohledu na to, zda byla výjimka vyvolána či nikoliv.
    # Tento statement je nepovinný.

Zachycení všech výjimek

try:
    # Kód, který může vyvolat různé výjimky.
except:
    # Tady zachytáváme všechny!!! výjimky.

Poznámka: Tento přístup většinou není moc vhodný. Obzvláště pokud jediným příkazem v except bloku je “pass“, pak jde o velice nešťastnou kombinaci. Je třeba si uvědomit, že skoro vždycky existuje možnost, že se vyskytne výjimka, kterou neočekáváme. A díky obecnému “except” s příkazem “pass” se tak o ní vůbec nedozvíme! Zároveň je třeba pamatovat na to, že uživatel se může kdykoli rozhodnout, že pomocí Ctrl+C vyvolá výjimku “KeyboardInterrupt” a ukončí program. Jaké však bude jeho překvapení, když se po stisku této kombinace nic nestane a program vesele pokračuje dál v konzumaci procesorového času.

Vyvolání výjimky v Pythonu

raise JmenoVyvolavaneVyjimky('Neco se pokazilo')

Počet parametrů vyvolávané výjimky závisí na tom, kolik parametrů výjimka přijímá.

Definování vlastní výjimky

class DivnaVyjimka(Exception):
    """
    Divna vyjimka, která by se nemela nikdy objevit.
    Prvni a jediny parametr je libovolne cislo nebo text.
    """

    def __init__(self, hodnota):
        self.hodnota = hodnota

    def __str__(self):
        return repr(self.value)

Konstruktor výjimky __init__ v našem případě akorát uloží hodnotu parametru do objektu výjimky.
Funkce __str__() se postará o informační textovou reprezentaci výjimky.
Funkce repr() vrátí řetězec obsahující tisknutelnou reprezentaci objektu, který byl předán jako parametr. V našem případě, kdy očekáváme jen řetězec nebo číslo, by stačilo místo repr() použít funkci str().

Znovu-vyvolání výjimky

Výjimka se dá opětovně vyvolat pomocí příkazu “raise” bez parametrů.
Toto se může hodit, pokud chcete provést nějaké lokální ošetření chybového stavu (udělat rollback v databázi, uzavřít soubor, …) ale zároveň chcete výjimku propagovat dále.

try:
    # Prace s databazi
except (NejakaDatabaseError):
    # Udelame rollback, pripadne jine osetreni chyby
    raise

Cena vyvolání výjimky

Vyvolané výjimky nejsou zadarmo, je s nimi spojena určitá režie.
Pokud však k vyvolání výjimky nedojde, je try/except blok velice efektivní!
V určitých případech může být, s ohledem na efektivitu, výhodnější mít v kódu try/except, než if podmínku.
Příklad: Uvažujte, že máte funkci na výpočet určité matematické rovnice. Během výpočtu může dojít k dělení nulou. Četnost dělení nulou je však zhruba 1 na 10 000 zavolání funkce. V takovém případě bude zřejmě výhodnější odstranit podmínku “if jmenovatel:” a místo toho vložit blok do try/except. Dá se totiž docela dobře předpokládat, že jedna vyvolaná a zachycená výjimka na 10 000 zavolání je pořád efektivnější, než 9 999 zbytečných zpracování if podmínky.

Na co je třeba pamatovat

Pokud je v try bloku vyvolána výjimka a při jejím zpracování v except bloku je vyvolána další výjimka (Například když se budete snažit uzavřít zdroj, který nebyl nikdy alokován, protože při jeho alokaci byla vyvolána původní výjimka.), tak pouze tato druhá výjimka (ta z except bloku) se bude propagovat dál a zcela tak překryje tu původní!

Pár tipů k výjimkám

  • Snažte se do bloku try vkládat pouze relevantní kód. Snadněji pak odhalíte, který příkaz výjimku vyvolal a nestane se, že budete hledat chybu v kódu, který ji neobsahuje.
  • Výjimky můžete pohodlně logovat, pomocí standardního Pythoního modulu logging.
  • Detailnější informace o chybě můžete získat z tracebacku. Pro práci s ním můžete využít standardního modulu traceback. Pak si můžete traceback zpracovat  během ošetření výjimky a například ho uložit do souboru nebo odeslat emailem.

Odkazy

Defenzivní programování
Dokumentace k výjimkám
Dokumentace modulu logging
Článek Python Exception Handling Techniques
Článek Good catch all exceptions

Comments are closed.