PyLadies Brno

Výjimky

Výpisy chyb

Na začátku našeho povídání o chybách si ukážeme, jak se vypisuje chyba, která nastane v zanořené funkci:
def vnejsi_funkce():
    return vnitrni_funkce(0)

def vnitrni_funkce(delitel):
    return 1 / delitel

print(vnejsi_funkce())
Traceback (most recent call last):          
  File "/tmp/ukazka.py", line 7, in 
    print(vnejsi_funkce())
  File "/tmp/ukazka.py", line 2, in vnejsi_funkce
    return vnitrni_funkce(0)
  File "/tmp/ukazka.py", line 5, in vnitrni_funkce
    return 1 / delitel
ZeroDivisionError: division by zero
Všimni si, že každá funkce, jejíš volání vedlo k chybě, je uvedena ve výpisu. Skutečná chyba (tedy místo, které musíme opravit) je pravděpodobně poblíž některého z těchto volání. V našem případě bychom asi neměly volat vnitrni_funkce s argumentem 0 – a nebo by vnitrni_funkce měla být na nulu připravená a dělat v tomto případě něco jiného.

Vyvolání chyby

VELIKOST_POLE = 20

def over_cislo(cislo):
    if 0 < cislo <= VELIKOST_POLE:
        raise ValueError('Čislo {n} není v poli!'.format(n=cislo))
Všechny typy výjimek, které jsou zabudované v Pythonu, jsou popsané v dokumentaci Pythonu . Pro nás jsou důležité tyto:
BaseException
 ├── SystemExit                     vyvolá funkce exit()
 ├── KeyboardInterrupt              vyvolána po stisknutí Ctrl+C
 ╰── Exception
      ├── ArithmeticError
      │    ╰── ZeroDivisionError    dělení nulou
      ├── AssertionError            nepovedený příkaz `assert`
      ├── AttributeError            neexistující atribut, např. 'abc'.len
      ├── ImportError               nepovedený import
      ├── LookupError
      │    ╰── IndexError           neexistující index, např. 'abc'[999]
      ├── NameError                 použití neexistujícího jména proměnné
      │    ╰── UnboundLocalError    použití proměnné, která ještě nebyla nastavená
      ├── SyntaxError               špatná syntaxe – program je nečitelný/nepoužitelný
      │    ╰── IndentationError     špatné odsazení
      │         ╰── TabError        kombinování mezer a tabulátorů
      ├── TypeError                 špatný typ, např. len(9)
      ╰── ValueError                špatná hodnota, např. int('xyz')

Ošetření chyby

def nacti_cislo():
    odpoved = input('Zadej číslo: ')
    try:
        cislo = int(odpoved)
    except ValueError:
        print('To nebylo číslo!')
Pokud odchytáváme obecnou výjimku, chytnou se i všechny podřízené typy výjimek – například except ArithmeticError: zachytí i ZeroDivisionError. A except Exception: zachytí všechny výjimky, které běžně chceme zachytit.

Všechny příkazy

try:
    neco_udelej()
except ValueError:
    print('Tohle se provede, pokud nastane ValueError')
except NameError:
    print('Tohle se provede, pokud nastane NameError')
except Exception:
    print('Tohle se provede, pokud nastane jiná chyba')
    # (kromě SystemExit a KeyboardInterrupt, ty chytat nechceme)
else:
    print('Tohle se provede, pokud chyba nenastane')
finally:
    print('Tohle se provede vždycky')

Úkol

Doplň do 1-D piškvorek ošetření chyby, která nastane když uživatel nezadal číslo.
def tah_hrace(pole):
    while True:
        try:
            pozice = int(input('Kam chceš hrát? '))
        except ValueError:
            print('To není číslo!')
        else:
            if pozice < 0 or pozice >= len(pole):
                print('Nemůžeš hrát venku z pole!')
            elif pole[pozice] != '-':
                print('Tam není volno!')
            else:
                break

    pole = pole[:pozice] + 'o' + pole[pozice + 1:]
    return pole


print(tah_hrace('-x----'))