Python Language
Wyjątki
Szukaj…
Wprowadzenie
Błędy wykryte podczas wykonywania są nazywane wyjątkami i nie są bezwarunkowo śmiertelne. Większość wyjątków nie jest obsługiwana przez programy; możliwe jest pisanie programów obsługujących wybrane wyjątki. W Pythonie istnieją specyficzne funkcje do radzenia sobie z wyjątkami i logiką wyjątków. Ponadto wyjątki mają bogatą hierarchię typów, wszystkie dziedziczące po typie BaseException
.
Składnia
- zgłosić wyjątek
- podbić # ponownie podnieść wyjątek, który został już zgłoszony
- zgłaszaj wyjątek od przyczyny # Python 3 - ustaw przyczynę wyjątku
- zgłasza wyjątek od None # Python 3 - pomija wszystkie konteksty wyjątków
- próbować:
- oprócz [typów wyjątków] [ jako identyfikatora ] :
- jeszcze:
- Wreszcie:
Zgłaszanie wyjątków
Jeśli kod napotka warunek, którego nie potrafi obsłużyć, taki jak niepoprawny parametr, powinien wygenerować odpowiedni wyjątek.
def even_the_odds(odds):
if odds % 2 != 1:
raise ValueError("Did not get an odd number")
return odds + 1
Łapanie wyjątków
Użyj try...except:
aby złapać wyjątki. Powinieneś podać jak najdokładniejszy wyjątek:
try:
x = 5 / 0
except ZeroDivisionError as e:
# `e` is the exception object
print("Got a divide by zero! The exception was:", e)
# handle exceptional case
x = 0
finally:
print "The END"
# it runs no matter what execute.
Określona klasa wyjątków - w tym przypadku ZeroDivisionError
- przechwytuje każdy wyjątek należący do tej klasy lub dowolnej podklasy tego wyjątku.
Na przykład ZeroDivisionError
jest podklasą ArithmeticError
:
>>> ZeroDivisionError.__bases__
(<class 'ArithmeticError'>,)
I tak następujące elementy nadal będą ZeroDivisionError
:
try:
5 / 0
except ArithmeticError:
print("Got arithmetic error")
W końcu uruchamiam kod czyszczący
Czasami możesz chcieć, aby coś się wydarzyło bez względu na wyjątek, na przykład, jeśli musisz wyczyścić niektóre zasoby.
finally
blok klauzuli try
nastąpi, niezależnie od tego, czy zgłoszono wyjątki.
resource = allocate_some_expensive_resource()
try:
do_stuff(resource)
except SomeException as e:
log_error(e)
raise # re-raise the error
finally:
free_expensive_resource(resource)
Ten wzorzec jest często lepiej obsługiwany w menedżerach kontekstu (przy użyciu instrukcji with
).
Ponowne zgłaszanie wyjątków
Czasami chcesz złapać wyjątek tylko w celu jego sprawdzenia, np. Do celów logowania. Po inspekcji chcesz, aby wyjątek był kontynuowany tak jak wcześniej.
W takim przypadku wystarczy użyć instrukcji raise
bez parametrów.
try:
5 / 0
except ZeroDivisionError:
print("Got an error")
raise
Pamiętaj jednak, że ktoś znajdujący się wyżej na stosie wywołującego może nadal wychwycić wyjątek i jakoś sobie z nim poradzić. Wykonane wyjście może być w tym przypadku uciążliwe, ponieważ nastąpi w każdym przypadku (złapane lub nie złapane). Lepszym pomysłem może być zgłoszenie innego wyjątku, zawierającego komentarz na temat sytuacji, a także oryginalny wyjątek:
try:
5 / 0
except ZeroDivisionError as e:
raise ZeroDivisionError("Got an error", e)
Ma to jednak tę wadę, że redukuje ślad wyjątku do dokładnie tego raise
podczas gdy raise
bez argumentu zachowuje oryginalny ślad wyjątku.
W Pythonie 3 możesz zachować oryginalny stos za pomocą raise
- from
składni:
raise ZeroDivisionError("Got an error") from e
Wyjątki łańcuchowe z podwyżką z
Podczas obsługi wyjątku możesz chcieć zgłosić inny wyjątek. Na przykład, jeśli podczas odczytu pliku pojawi się błąd IOError
, możesz zamiast tego zgłosić błąd specyficzny dla aplikacji, aby przedstawić go użytkownikom swojej biblioteki.
Możesz połączyć wyjątki, aby pokazać, jak przebiegała obsługa wyjątków:
>>> try:
5 / 0
except ZeroDivisionError as e:
raise ValueError("Division failed") from e
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: Division failed
Hierarchia wyjątków
Obsługa wyjątków odbywa się w oparciu o hierarchię wyjątków, określoną przez strukturę dziedziczenia klas wyjątków.
Na przykład IOError
i OSError
są podklasami EnvironmentError
. Kod wychwytujący IOError
nie przechwytuje OSError
. Jednak kod przechwytujący błąd EnvironmentError
wyłapuje zarówno błąd IOError
jak i OSError
.
Hierarchia wbudowanych wyjątków:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
Wyjątkiem są również obiekty
Wyjątkami są zwykłe obiekty Pythona, które dziedziczą z wbudowanego BaseException
. Skrypt Pythona może użyć instrukcji raise
do przerwania wykonywania, powodując, że Python wydrukuje ślad stosu wywołania w tym punkcie i reprezentację wystąpienia wyjątku. Na przykład:
>>> def failing_function():
... raise ValueError('Example error!')
>>> failing_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in failing_function
ValueError: Example error!
co mówi, że błąd ValueError
z komunikatem 'Example error!'
został wywołany przez failing_function()
, która została wykonana w interprecie.
Kod telefoniczny może obsłużyć dowolny wyjątek, który może wywołać połączenie:
>>> try:
... failing_function()
... except ValueError:
... print('Handled the error')
Handled the error
Możesz zdobyć obiekty wyjątków, przypisując je do wyjątku except...
części kodu obsługi wyjątków:
>>> try:
... failing_function()
... except ValueError as e:
... print('Caught exception', repr(e))
Caught exception ValueError('Example error!',)
Pełna lista wbudowanych wyjątków Pythona wraz z ich opisami znajduje się w dokumentacji Pythona: https://docs.python.org/3.5/library/exceptions.html . A oto pełna lista uporządkowana hierarchicznie: Hierarchia wyjątków .
Tworzenie niestandardowych typów wyjątków
Utwórz klasę dziedziczącą po Exception
:
class FooException(Exception):
pass
try:
raise FooException("insert description here")
except FooException:
print("A FooException was raised.")
lub inny typ wyjątku:
class NegativeError(ValueError):
pass
def foo(x):
# function that only accepts positive values of x
if x < 0:
raise NegativeError("Cannot process negative numbers")
... # rest of function body
try:
result = foo(int(input("Enter a positive integer: "))) # raw_input in Python 2.x
except NegativeError:
print("You entered a negative number!")
else:
print("The result was " + str(result))
Nie łap wszystkiego!
Chociaż często kuszące jest uchwycenie każdego Exception
:
try:
very_difficult_function()
except Exception:
# log / try to reconnect / exit gratiously
finally:
print "The END"
# it runs no matter what execute.
Lub nawet wszystko (w tym BaseException
i wszystkie jego dzieci, w tym Exception
):
try:
even_more_difficult_function()
except:
pass # do whatever needed
W większości przypadków jest to zła praktyka. Może wychwycić więcej niż zamierzony, na przykład SystemExit
, KeyboardInterrupt
i MemoryError
- z których każdy powinien być ogólnie traktowany inaczej niż zwykłe błędy systemowe lub logiczne. Oznacza to również, że nie ma jasnego zrozumienia tego, co wewnętrzny kod może zrobić źle i jak prawidłowo odzyskać ten stan. Jeśli wychwytujesz każdy błąd, nie wiesz, jaki błąd wystąpił ani jak go naprawić.
Jest to częściej określane jako „maskowanie błędów” i należy go unikać. Pozwól programowi zawiesić się zamiast po cichu zawieść lub nawet gorzej, zawodząc przy głębszym poziomie wykonania. (Wyobraź sobie, że to system transakcyjny)
Zwykle te konstrukcje są używane na bardzo zewnętrznym poziomie programu i będą rejestrować szczegóły błędu, aby błąd można było naprawić lub błąd można rozwiązać bardziej szczegółowo.
Łapanie wielu wyjątków
Istnieje kilka sposobów na złapanie wielu wyjątków .
Pierwszym z nich jest utworzenie krotki typów wyjątków, które chcesz przechwycić i obsługiwać w ten sam sposób. Ten przykład spowoduje, że kod zignoruje wyjątki KeyError
i AttributeError
.
try:
d = {}
a = d[1]
b = d.non_existing_field
except (KeyError, AttributeError) as e:
print("A KeyError or an AttributeError exception has been caught.")
Jeśli chcesz obsługiwać różne wyjątki na różne sposoby, możesz podać osobny blok wyjątków dla każdego typu. W tym przykładzie nadal KeyError
i AttributeError
, ale obsługujemy wyjątki na różne sposoby.
try:
d = {}
a = d[1]
b = d.non_existing_field
except KeyError as e:
print("A KeyError has occurred. Exception message:", e)
except AttributeError as e:
print("An AttributeError has occurred. Exception message:", e)
Praktyczne przykłady obsługi wyjątków
Wprowadzane przez użytkownika
Wyobraź sobie, że chcesz, aby użytkownik wprowadził liczbę poprzez input
. Chcesz się upewnić, że wprowadzoną liczbą jest liczba. Możesz użyć try
/ except
tego:
while True:
try:
nb = int(input('Enter a number: '))
break
except ValueError:
print('This is not a number, try again.')
Uwaga: Python 2.x użyłby zamiast tego raw_input
; funkcja input
istnieje w Pythonie 2.x, ale ma inną semantykę. W powyższym przykładzie input
akceptują również wyrażenia, takie jak 2 + 2
które są wynikiem liczby.
Jeśli danych wejściowych nie można przekonwertować na liczbę całkowitą, ValueError
jest błąd ValueError
. Możesz to złapać za except
. Jeśli nie zostanie zgłoszony żaden wyjątek, break
wyskoczy z pętli. Po pętli nb
zawiera liczbę całkowitą.
Słowniki
Wyobraź sobie, że wykonujesz iterację po liście kolejnych liczb całkowitych, takich jak range(n)
, i masz listę słowników d
która zawiera informacje o czynnościach, które należy wykonać, gdy napotkasz określone liczby całkowite, na przykład pomiń d[i]
następne .
d = [{7: 3}, {25: 9}, {38: 5}]
for i in range(len(d)):
do_stuff(i)
try:
dic = d[i]
i += dic[i]
except KeyError:
i += 1
KeyError
zostanie KeyError
, gdy spróbujesz uzyskać wartość ze słownika dla klucza, który nie istnieje.
Jeszcze
Kod w bloku else zostanie uruchomiony tylko wtedy, gdy kod nie podniesie wyjątków w bloku try
. Jest to przydatne, jeśli masz kod, którego nie chcesz uruchamiać, jeśli zostanie zgłoszony wyjątek, ale nie chcesz, aby wyjątki zgłaszane przez ten kod były wychwytywane.
Na przykład:
try:
data = {1: 'one', 2: 'two'}
print(data[1])
except KeyError as e:
print('key not found')
else:
raise ValueError()
# Output: one
# Output: ValueError
Zauważ, że tego rodzaju else:
nie można łączyć z if
klauzula else zaczyna się od elif
. Jeśli masz następujące elementy, if
muszą one pozostać wcięte poniżej tego else:
:
try:
...
except ...:
...
else:
if ...:
...
elif ...:
...
else:
...