https://docs.python.org/3/library/exceptions.html
https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement
Zalety wyjątków opartych na klasach.
# Nowe podejście - wyjątek to klasa wywiedziona z klasy Exception.
# Do nazwy dajemy suffix "Error", o ile wyjątek to błąd.
# Dla ostrzeżeń w nazwie dodajemy "Warning".
class MyError(Exception):
"""Warto stworzyć wiersz dokumentujący."""
pass
raise MyError("message") # wywołanie wyjątku
exception = MyError("a", "b") # instancja wyjątku
# W atrybucie args znajduje się krotka argumentów konstruktora domyślnego.
print(exception.args) # ('a', 'b')
# Domyślnie str() wyświetla zawartość atrybutu args.
print(exception) # ('a', 'b')
try:
raise MyError("message") # instancja
#except MyError:
except MyError as exception:
print("przechwycenie MyError")
print(exception.args)
Jeżeli wyjątek został wyzwolony z argumentami, to w instancji wyjątku są domyślnie przechowywane jako krotka w atrybucie args. Dla wygody klasa Exception definiuje metodę __str__() wyświetlającą argumenty, dzięki czemu nie musimy bezpośrednio odwoływać się do atrybutu args.
Można zdefiniować własny konstruktor wyjątku (__init__). Podobnie można określić własny sposób wyświetlania wyjątku (__str__).
class MyError(Exception):
def __init__(self, value): # nasz konstruktor wyjątku
self.value = value
def __str__(self): # zmiana sposobu wyświetlania wyjątku
return repr(self.value)
try:
raise MyError(2*2) # instancja wyjątku
except MyError as exception: # Py2.6+, Py3
# except MyError, exception: # Py2.6-
# Python 3 interpretuje przecinek jako oznaczający krotkę.
# Obiekt exception jest to zgłoszona instancja klasy MyError.
print("mam wyjątek, value {}".format(exception.value))
print("mam wyjątek, value {}".format(exception)) # jw, bo jest __str__
import sys
print("Zgłoszono wyjątek {}".format(sys.exc_info()))
Ostatnio zgłoszony i przechwycony wyjątek jest dostępny ogólnie, jako drugi argument krotki wyników wywołania sys.exc_info(), (typ, wartość, ślad), (type, value, traceback). Jeżeli żaden wyjątek nie jest obsługiwany, to zwracana krotka ma postać (None, None, None).
Do jawnego wywoływania wyjątków służy instrukcja raise.
# Składnia.
# Zgłoszenie instancji klasy [najbardziej typowe].
# raise instancja_wyjątku
raise IndexError()
raise IndexError("message")
# Zgłoszenie obiektu klasy - instancja klasy zostanie utworzona automatycznie.
# raise klasa_wyjątku
raise IndexError
# Ponowne zgłoszenie ostatniego wyjątku.
raise
# Zgłoszenie nowego wyjątku z wykorzystaniem kontekstu starego wyjątku.
raise new_exception from old_exception # Py3
raise new_exception from None
# Dla wbudowanych wyjątków mamy równoważne formy.
raise KeyError
raise KeyError()
# Utworzenie instancji wyjątku z wyprzedzeniem.
exception = IndexError()
raise exception
Instrukcja assert jest składniowym skrótem dla często wykorzystywanego w debugowaniu wzorca z instrukcją raise. Najczęściej wykorzystuje się ją do weryfikowania warunków programu w czasie jego tworzenia (tzw. sytuacje niemożliwe). Pomaga wcześnie wykrywać pewne błędy w programie. Ponadto assert działa jako dokumentacja dla innych deweloperów czytających kod, pozwala lepiej zrozumieć stan systemu w danym punkcie programu.
Nie powinno się używać assert do obsługi błędów użytkownika, czy innych błędów pojawiających się w trakcie pracy programu.
Instrukcję assert można usunąć z kodu bajtowego skompilowanego programu, jeżeli w wierszu poleceń Pythona użyjemy opcji -O (python -O main.py), tym samym optymalizując program.
# Składnia podstawowa.
assert expression
# Równoważny kod.
if __debug__:
if not expression:
raise AssertionError()
# Składnia rozszerzona.
assert expression1, expression2
# Równoważny kod.
if __debug__:
if not expression1:
raise AssertionError(expression2)
# expression2 to zwykle komunikat (string)
>>> x = 1 >>> assert x == 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError >>> assert x == 0, "x have to be zero" Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError: x have to be zero >>>
Kiedy except z instrukcji try wymienia klasę nadrzędną, przechwytuje instancje tej klasy, a także instancje wszystkich jej klas podrzędnych.
Warto korzystać z wyjątków wbudowanych i rzucać je w sytuacjach podobnych do tych, w jakich robi to interpreter. Nie ma jednak mechanizmów zabezpieczających przed rzuceniem nieodpowiedniego wyjątku przez kod użytkownika. Można dziedziczyć z wyjątków wbudowanych, ale należy unikać dziedziczenia wielobazowego ze względu na specyficzne zarządzanie pamięcią w wyjątkach i możliwe konflikty.
W Pythonie 3.11 pojawiły się 'grupy wyjątków' (exception groups), które są używane kiedy trzeba jednocześnie rzucić wiele niepowiązanych ze sobą wyjątków. Do stworzenia grupy wyjatków wykorzystuje się dziedziczenie z klasy 'ExceptionGroup'.
>>> import exceptions # Py2.6+ >>> help(exceptions) # drzewo klas
>>> import builtins # Py3, built-in functions, exceptions, and other objects >>> help(builtins)
BaseException
+-- BaseExceptionGroup (Py3.11)
+-- SystemExit # rzucany przez sys.exit()
+-- KeyboardInterrupt # rzucany po naciśnięciu Ctrl+C lub Delete
+-- GeneratorExit # rzucany przez generator.close() i coroutine.close()
+-- Exception # klasa bazowa dla wyjątków wbudowanych i wyjątków użytkownika
+-- StopIteration
+-- StandardError
| +-- ArithmeticError
| +-- AssertionError
| +-- AttributeError
| +-- NameError
| +-- SyntaxError
| +-- IndentationError
| +-- TypeError
| +-- ValueError
| +-- ImportError
| +-- MemoryError
| +-- RuntimeError
| +-- NotImplementedError
| +-- EnvironmentError
| +-- IOError
| +-- OSError
| +-- LookupError
| +-- IndexError
| +-- KeyError
| +-- ExceptionGroup [BaseExceptionGroup] (Py3.11)
| +-- ...
+-- Warning
+-- DeprecationWarning [PEP 387]
+-- UserWarning
+-- SyntaxWarning
+-- RuntimeWarning
+-- ...
Wybrane wyjątki.