Wyjątki

Wprowadzenie

Wyjątki pojawiają się, gdy w twoim programie dzieje się coś wyjątkowego. Na przykład, co zrobić, gdy chcesz otworzyć pewien plik, a okazuje się, że on nie istnieje? Albo co wtedy, gdy przypadkiem w trakcie działania programu go usunąłeś? Właśnie takimi sytuacjami zajmują się wyjątki.

Podobnie, co wtedy, gdy twój program posiada jakieś nieprawidłowe wyrażenia? Python w takim wypadku zgłasza się i mówi, że gdzieś jest błąd.

Błędy

Rozważmy proste wywołanie polecenia print. Co się stanie, gdy przypadkiem zapiszemy print jako Print? W tym wypadku Python zgłasza błąd nazwy polecenia.

>>> Print (1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print (1, 2, 3)
(1, 2, 3)

Zobacz, że Python pokazał rodzaj błędu (NameError) oraz miejsce, w którym ten błąd wystąpił. Oto, jak działa Pythonowa kontrola błędów.

Wyjątki

Spróbujemy uzyskać wpis od użytkownika. W tym przykładzie, zamiast coś wpisywać, naciśnij ctrl-d i zobacz, co się stanie.

>>> s = raw_input('Wpisz coś ---> ')
Wpisz coś ---> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EOFError

Python zgłasza błąd typu EOFError, co oznacza, że znalazł symbol końca pliku (który jest reprezentowany przez ctrl-d) w miejscu, w którym on nie powinien występować (EOF - End Of File — ang. koniec pliku).

Obsługa wyjątków

Możemy obsłużyć wyjątki używając wyrażenia try...except.... Do bloku try wprowadzamy nasze polecenia testowe, a w except wpisujemy wszystko, co ma się zdarzyć, gdy wystąpi błąd.

Przykład:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: try_except.py

try:
    tekst = raw_input('Wpisz coś ---> ')
except EOFError:
    print 'Dlaczego użyłeś znaku końca pliku?'
except KeyboardInterrupt:
    print 'Przerwałeś operację.'
else:
    print 'Wpisałeś %s' % tekst

Rezultat:

$ python try_except.py
Wpisz coś ---> Dlaczego użyłeś znaku końca pliku?
$ python try_except.py
Wpisz coś ---> ^CPrzerwałeś operację.
$ python try_except.py
Wpisz coś ---> tekst nie wywołujący błędów
Wpisałeś tekst nie wywołujący błędów

Za pierwszym razem naciśnięto ctrl-d, za drugim ctrl-c.

Jak to działa:

Wstawiamy wszystkie polecenia, które mogą powodować błędy, do bloku try, a następnie do except dopisujemy rodzaje błędów i sposoby radzenia sobie z nimi. except może odnosić się do jednego błędu lub do kilku, jeżeli są zapisane w formie krotki. W przypadku, gdy rodzaj błędu nie zostanie określony, except odniesie się do wszystkich błędów.

Pamiętaj, że zawsze musi być co najmniej jedno except przy każdym try. W przeciwnym razie, jaki byłby sens wstawiania bloku try?

Gdy nie obsłużymy błędu za pomocą except, wtedy Python domyślnie zatrzymuje program i wyświetla komunikat o błędzie. Już to wcześniej zobaczyliśmy.

Do bloku try możesz dołączyć też blok else, który zostanie wywołany, gdy nie wystąpi żaden wyjątek.

Zgłaszanie wyjątków

Możesz zgłosić wyjątki używając wyrażenia raise poprzez podanie nazwy wyjątku oraz obiektu, który ma się pojawić.

Błąd lub wyjątek, który chcesz zgłosić, powinien być klasą, która bezpośrednio lub pośrednio musi być pochodną klasy Exception.

Przykład:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: zglaszanie.py

class WyjatekKrotkiegoWpisu(Exception):
    '''Klasa wyjątku zdefiniowana przez użytkownika.'''
    def __init__(self, dlugosc, conajmniej):
        Exception.__init__(self)
        self.dlugosc = dlugosc
        self.conajmniej = conajmniej

try:
    tekst = raw_input('Wpisz coś ---> ')
    if len(tekst) < 3:
        raise WyjatekKrotkiegoWpisu(len(tekst), 3)
    # Pozostałe polecenia mogą tu być normalnie wpisane.
except EOFError:
    print 'Dlaczego użyłeś znaku końca pliku?'
except WyjatekKrotkiegoWpisu, wkw:
    print 'WyjatekKrotkiegoWpisu: Wpis miał długość %d, wymagane co najmniej %d.'\
        % (wkw.dlugosc, wkw.conajmniej)
else:
    print 'Żaden wyjątek nie został zgłoszony.'

Rezultat:

$ python zglaszanie.py
Wpisz coś ---> a
WyjatekKrotkiegoWpisu: Wpis miał długość 1, wymagane co najmniej 3.
$ python zglaszanie.py
Wpisz coś ---> abc
Żaden wyjątek nie został zgłoszony.

Jak to działa:

Tutaj stworzyliśmy własny typ wyjątku o nazwie WyjatekKrotkiegoWpisu. Ma on dwa pola — dlugosc, czyli długość wpisanego tekstu oraz conajmniej, czyli minimalna długość, jaką program oczekuje.

W wyrażeniu except wymieniamy klasę błędu oraz zmienną, do której zostanie zapisany obiekt błędu / wyjątku. To jest analogiczne do parametrów i argumentów przy wywoływaniu funkcji. Wewnątrz tego właśnie wyrażenia except używamy pól dlugosc i conajmniej do przekazania odpowiedniej informacji użytkownikowi.

try...finally...

Przypuśćmy, że odczytujesz jakiś plik w swoim programie. W jaki sposób zapewnić, że plik zostanie zamknięty, niezależnie od tego, czy wyjątek został zgłoszony, czy nie? W tym celu służy blok finally. Oczywiście możesz użyć wyrażenia except razem z blokiem finally dla tego samego bloku try.

Przykład:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: try_finally.py

import time

try:
    plik = open('wierszyk.txt')
    while True:
        wers = plik.readline()
        if len(wers) == 0:
            break
        print wers,
        time.sleep(2) # Żeby program się na chwilę zatrzymał.
except KeyboardInterrupt:
    print '!! Przerwałeś odczytywanie pliku.'
finally:
    plik.close()
    print '(Sprzątanie: Zamknięto plik)'

Rezultat:

$ python try_finally.py
Programming is fun
When the work is done
if you wanna make your work also fun:
^C!! Przerwałeś odczytywanie pliku.
(Sprzątanie: Zamknięto plik)

Jak to działa:

Wykonaliśymy prosty odczyt tekstu z pliku, ale dodatkowo dodaliśmy dwusekundową przerwę po wypisaniu na ekran każdej linijki dzięki funkcji time.sleep, aby program pracował powoli. (Python z natury jest bardzo szybki.) Jeszcze w trakcie odczytu nacisnęliśmy ctrl-c i przerwaliśmy działanie programu.

Wyjątek dla błędu KeyboardInterrupt został wyświetlony i program zakończył swoje działanie. Jednakże tuż przed końcem programu został wykonany blok finally. Mamy więc pewność, że plik został poprawnie zamknięty.

Wyrażenie with

Uzyskiwanie zasobów w bloku try oraz uwalnianie ich w finally to powszechne działanie. Jednak istnieje również wyrażenie with, które pozwala to zrobić w prostszy sposób:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: with.py

from __future__ import with_statement # Niepotrzebne w Pythonie 2.6 i wyższych (w szczególności 3.x)

with open('wierszyk.txt') as plik:
    for wers in plik:
        print wers,

Jak to działa:

Wynik powinien być taki sam, jak w powyższym przykładzie. Różnicą jest to, że tym razem używamy funkcji open razem z wyrażeniem with — nie przejmujemy się zamykaniem pliku, gdyż with open robi to automatycznie. (W Pythonie 2.5 with musi być najpierw zaimportowane z modułu __future__.)

Tłem tego wszystkiego jest protokół używany przez with. W tym wypadku zajmuje się plikiem przekazanym przez funkcję open.

Polecenie with zawsze wywołuje funkcję plik.__enter__ przed wykonaniem swojego bloku oraz zawsze na koniec wywołuje funkcję plik.__exit__.

Dlatego zadanie kodu, który wpisaliśmy do bloku finally jest już automatycznie wykonane przez metodę __exit__. To właśnie pomaga nam ominąć konieczność używania cały czas wyrażeń try...finally....

Dalsza dyskusja na ten temat jest już poza zakresem tej książki, dlatego dla pełnych wyjaśnień zajrzyj do dokumentu PEP 343.

Podsumowanie

Poznaliśmy użycie wyrażeń try...except... oraz try...finally.... Dodatkowo dowiedzieliśmy się, jak tworzyć i zgłaszać wyjątki.

Dalej poznamy bibliotekę standardową Pythona.

results matching ""

    No results matching ""