Podsekcje

 
8. Błędy i wyjątki

Aż do teraz, komunikaty o błędach nie były często wspominane, ale jeśli próbowałeś uruchamiać wszystkie przykłady prawdopodobnie zobaczyłeś parę. Istnieją (przy najmniej) dwa rozróżnialne rodzaje błędów: błędy składni i wyjątki.

 
8.1 Błędy składni

Błędy składniowe, znane również jako błędy parsingu, są być może najbardziej powszechnym rodzajem zażaleń, które otrzymuje się podczas nauki Pythona:

>>> while 1 print 'Cześć świecie'
  File "<stdin>", line 1
    while 1 print 'Cześć świecie'
                ^
SyntaxError: invalid syntax

Parser powtarza na wyjściu obraźliwy dla niego wiersz i wyświetla małą ,,strzałkę'', wskazującą na najwcześniejszy punkt w wierszu gdzie znaleziono błąd. Błąd spowodowany został (lub przynajmniej wykryty w) przez składnik poprzedzający strzałkę: w tym przykładzie, błąd wykryty został na słowie kluczowym print, z powodu braku znaku dwukropka (":") przed tą instrukcją. Nazwa pliku i numer linii są również wyświetlane, aby wiedzieć, gdzie szukać błędu, w przypadku gdy na wejściu znajdował się skrypt.

 
8.2 Wyjątki

Nawet gdy wyrażenie jest składniowo poprawne, może spowodować błąd podczas próby wykonania go. Błędy wykryte podczas wykonania nazywane są wyjątkami i niekoniecznie muszą zakończyć program: za chwilę nauczysz się jak radzić sobie z nimi w programach Pythona. Większość wyjątków nie jest obsługiwana przez programy i objawiają się w komunikatach o błędzie, jak poniżej:

>>> 10 * (1/0)
Traceback (innermost last):
  File "<stdin>", line 1
ZeroDivisionError: integer division or modulo
>>> 4 + szynka*3
Traceback (innermost last):
  File "<stdin>", line 1
NameError: szynka
>>> '2' + 2
Traceback (innermost last):
  File "<stdin>", line 1
TypeError: illegal argument type for built-in operation
(ZeroDivisionError: dzielenie całkowite lub operacja modulo)

(TypeError: nieprawidłowy typ argumentu operacji wbudowanej)

Ostatnia linia komunikatu o błędzie pokazuje co się stało. Wyjątki są różnego typu, i ten typ wyświetlany jest jako część komunikatu. W powyższym przykładzie tymi typami są ZeroDivisionError, NameError i TypeError. Napis wyświetlany jako typ wyjątku jest nazwą wbudowanego w interpreter wyjątku, który się właśnie pojawił. Zachodzi to dla wszystkich wyjątków wbudowanych,, lecz niekoniecznie jest to prawdą dla wyjątków zdefiniowanych przez użytkownika (jakkolwiek, jest to użyteczna konwencja). Nazwy standardowych wyjątków są wbudowanymi identyfikatorami, a nie zastrzeżonymi słowami kluczowymi.

Reszta wiersza komunikatu w szczegółach opisuje to, co się stało. Interpretacja i znaczenie tego opisu zależy od typu wyjątku.

Pierwsza część komunikatu pokazuje kontekst, w jakim wyjątek się zdarzył, w postaci śladu ze stosu wywołań. Ogólnie mówiąc, kontekst zawiera ślady wywołań w postaci wierszy kodu, aczkolwiek nie pokaże wierszy podchodzących ze standardowego wejścia.

Opis biblioteki Pythona wyszczególnia wszystkie wbudowane wyjątki i przedstawia ich znaczenie.

 
8.3 Obsługa wyjątków

Możliwe jest napisanie programu, który obsługuje wybrane wyjątki. Spójrzmy na przykład poniżej, w którym użytkownik podaje dane na wejściu aż do momentu, kiedy zostanie wprowadzona poprawna liczba całkowita. Użytkownik może przerwać pracę (poprzez naciśnięcie Control-C lub dowolny sposobem, jaki jest możliwy poprzez klawiaturę w danym systemie operacyjnym). Przerwanie użytkownika sygnalizowane jest poprzez zgłoszenie wyjątku KeyboardInterrupt.

>>> while 1:
...     try:
...         x = int(raw_input("Proszę wprowadzić liczbę: "))
...         break
...     except ValueError:
...         print "Uch! to nie jest poprawna liczba! Spróbuj jeszcze raz..."
...

Oto jak działa instrukcja try:

Aby umożliwić obsługę wielu wyjątków, instrukcja try może posiadać więcej niż jedną klauzulę except. W takim przypadku, kod dla co najwyżej jednego wyjątku zostanie wykonany. Kody obsługi wyjątków wykonywane są tylko dla wyjątków, które zostały zgłoszone w odpowiadającej im części try, a nie w innych, sąsiednich częściach except. Klauzula except może zawierać nazwy wielu wyjątków, podanych w formie listy otoczonej nawiasami okrągłymi8.1:

... except (RuntimeError, TypeError, NameError):
...     pass

W ostatniej podanej klauzuli except można pominąć nazwę lub nazwy wyjątków w celu obsłużenia dowolnego wyjątku. Należy posługiwać się tą konstrukcją bardzo ostrożnie, ponieważ jest to sposób do łatwego zamaskowania prawdziwego wystąpienia błędu w programie! Można jej również użyć do wydrukowania komunikatu o błędzie i ponownie zgłosić wyłapany wyjątek (umożliwiając w ten sposób funkcji wywołującej wyłapanie zgłoszonego wyjątku):

import string, sys

try:
    f = open('mójplik.txt')
    s = f.readline()
    i = int(string.strip(s))
except IOError, (errno, strerror):
    print "Błąd I/O (%s): %s" % (errno, strerror)
except ValueError:
    print "Nie mogę przekształcić danej w liczbę całkowitą."
except:
    print "Nieobsługiwany błąd:", sys.exc_info()[0]
    raise

Instrukcja try ... except wyposażona jest w opcjonalną klauzulę else, która musi pojawić się za wszystkimi podanymi blokami except. Można po niej umieścić kod, który zostanie wykonany, jeżeli nie zostanie zgłoszony wyjątek:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'nie mogę otworzyć  pliku', arg
    else:
        print arg, 'ma', len(f.readlines()), 'linię/linii'
        f.close()

Wyjątek może pojawić się z przypisaną sobie wartością, która nazywana jest argumentem wyjątku. Obecność tej wartości i jej typ zależy od rodzaju wyjątku. Jeżeli chce się poznać tę wartość, należy podać nazwę zmiennej (lub listę zamiennych) za nazwą typu wyjątku w klauzuli except:

>>> try:
...     szynka()
... except NameError, x:
...     print 'nazwa', x, 'nie została zdefiniowana'
...
nazwa szynka nie została zdefiniowana

Argument ten (jeśli istnieje) pokazywany jest w ostatniej części («detail») komunikatu o niewyłapanym wyjątku.

Kody obsługujące wyjątek uruchamiane są nie tylko po zgłoszeniu wyjątku w ich klauzuli try, ale także w przypadku, gdy pojawią się one w instrukcjach try umieszczonych w wywoływanych funkcjach (nawet pośrednio):

>>> def to_sie_nie_uda():
...     x = 1/0
...
>>> try:
...     this_fails()
... except ZeroDivisionError, detale:
...     print 'Obsługa błędu wykonania programu:', detale
...
Obsługa błędu wykonania programu: integer division or modulo

 
8.4 Zgłaszanie wyjątków

Instrukcja raise służy do wymuszania pojawienia się podanego wyjątku. Na przykład:

>>> raise NameError, 'HejTam'
Traceback (innermost last):
  File "<stdin>", line 1
NameError: HejTam

Pierwszy argument instrukcji raise służy do podania nazwy wyjątku. Opcjonalny drugi argument jest jego wartością (argumentem wyjątku).

 
8.5 Wyjątki definiowane przez użytkownika

W programach Pythona można tworzyć swoje wyjątki poprzez przypisanie napisu do zmiennej lub stworzenie nowej klasy wyjątku. 8.2 Na przykład:

>>> class NowyWyjatek:
...     def __init__(self, wartosc):
...         self.wartosc = wartosc
...     def __str__(self):
...         return `self.wartosc`
...
>>> try:
...     raise NowyWyjatek(2*2)
... except NowyWyjatek, w:
...     print 'Zgłoszono nowy wyjątek o wartości:', w.wartosc
...
Zgłoszono nowy wyjątek o wartości: 4
>>> raise NowyWyjatek, 1
Traceback (innermost last):
  File "<stdin>", line 1
__main__.NowyWyjatek: 1

W wielu standardowych modułach używa się tego sposobu opisania błędów, które mogą pojawić się w zdefiniowanych w nich funkcjach.

Więcej informacji o klasach można znaleźć w rozdziale 9 «Klasy».

 
8.6 Jak posprzątać po bałaganiarzu?

Instrukcja try posiada jeszcze jedną, opcjonalną klauzulę, która służy do definiowania działań, mających na celu dokonanie koniecznych pod wszelkimi względami porządków. Na przykład:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print 'Żegnaj, świecie!'
...
Żegnaj, świecie!
Traceback (innermost last):
  File "<stdin>", line 2
KeyboardInterrupt

Klauzula finally jest wykonywana niezależnie od tego, czy pojawił sie wyjątek, czy też nie. Kod zawarty w tym bloku jest również wykonywany, gdy blok try zostanie ,,opuszczony'' za pomocą instrukcji break lub return.

Instrukcja try musi posiadać co najmniej jeden blok except lub jeden blok finally, ale nigdy oba równocześnie.



... okrągłymi8.1
Czyli w formie krotki .
... wyjątku.8.2
O klasach, już w następnym rozdziale...
Zajrzyj do Informacji na temat tej publikacji... aby pomóc w jej rozwoju.