Python Language
undantag
Sök…
Introduktion
Fel som upptäcks under körning kallas undantag och är inte ovillkorligt dödliga. De flesta undantag hanteras inte av program; det är möjligt att skriva program som hanterar utvalda undantag. Det finns specifika funktioner i Python för att hantera undantag och undantagslogik. Dessutom har undantag en rik BaseException
som alla ärver från BaseException
typen.
Syntax
- höja undantag
- höja # höja ett undantag som redan har tagits upp
- höja undantag från orsak # Python 3 - ange undantagsorsak
- höja undantag från None # Python 3 - undertryck alla undantagsförhållanden
- Prova:
- utom [undantagstyper] [ som identifierare ] :
- annan:
- till sist:
Höja undantag
Om din kod stöter på ett villkor som den inte vet hur hanteras, till exempel en felaktig parameter, bör den höja lämpligt undantag.
def even_the_odds(odds):
if odds % 2 != 1:
raise ValueError("Did not get an odd number")
return odds + 1
Fånga undantag
Använd try...except:
att få undantag. Du bör ange ett så exakt undantag som du kan:
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.
Undantagsklassen som är specificerad - i detta fall ZeroDivisionError
- fångar alla undantag som är i den klassen eller i någon underklass av det undantaget.
Till exempel är ZeroDivisionError
en underklass av ArithmeticError
:
>>> ZeroDivisionError.__bases__
(<class 'ArithmeticError'>,)
Och så kommer följande fortfarande att fånga ZeroDivisionError
:
try:
5 / 0
except ArithmeticError:
print("Got arithmetic error")
Kör rensningskod med äntligen
Ibland kanske du vill att något ska ske oavsett vilket undantag som hänt, till exempel om du måste städa upp vissa resurser.
Det finally
blocket av en try
kommer att ske oavsett om några undantag har tagits upp.
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)
Detta mönster hanteras ofta bättre med kontekthanterare (med hjälp av with
uttalandet ).
Återupprätta undantag
Ibland vill du ta ett undantag bara för att inspektera det, t.ex. för loggningsändamål. Efter inspektionen vill du att undantaget ska fortsätta sprida sig som det gjorde tidigare.
I det här fallet använder du helt enkelt raise
utan parametrar.
try:
5 / 0
except ZeroDivisionError:
print("Got an error")
raise
Tänk dock på att någon längre upp i anropsstacken fortfarande kan fånga undantaget och hantera det på något sätt. Den gjorda utgången kan vara en olägenhet i detta fall eftersom den kommer att hända i alla fall (fångad eller inte fångad). Så det kan vara en bättre idé att ta upp ett annat undantag med din kommentar om situationen såväl som det ursprungliga undantaget:
try:
5 / 0
except ZeroDivisionError as e:
raise ZeroDivisionError("Got an error", e)
Men detta har nackdelen att reducera undantagsspåret till exakt denna raise
medan raise
utan argument behåller det ursprungliga undantagsspåret.
I Python 3 kan du behålla den ursprungliga stacken med raise
- from
syntax:
raise ZeroDivisionError("Got an error") from e
Kedjeundantag med höjning från
I processen att hantera ett undantag kanske du vill ta upp ett annat undantag. Om du till exempel får en IOError
när du läser från en fil, kanske du vill ta upp ett applikationsspecifikt fel för att presentera för användare av ditt bibliotek istället.
Du kan kedja undantag för att visa hur hanteringen av undantag fortsatte:
>>> 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
Undantagshierarki
Undantagshantering sker baserat på ett undantagshierarki, bestämt av arvstrukturen för undantagsklasserna.
Till exempel är IOError
och OSError
båda underklasser av EnvironmentError
. Kod som fångar en IOError
kommer inte att fånga en OSError
. Kod som fångar en EnvironmentError
kommer dock att fånga både IOError
och OSError
.
Hierarkin med inbyggda undantag:
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
Undantag är också föremål
Undantag är bara vanliga Python-objekt som ärver från den inbyggda BaseException
. Ett Python-skript kan använda raise
att avbryta exekveringen, vilket får Python att skriva ut ett stackspår för samtalsstacken vid den punkten och en representation av undantagsinstansen. Till exempel:
>>> 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!
som säger att en ValueError
med meddelandet 'Example error!'
höjdes av vår failing_function()
, som kördes i tolkaren.
Ringkod kan välja att hantera alla typer av undantag som ett samtal kan höja:
>>> try:
... failing_function()
... except ValueError:
... print('Handled the error')
Handled the error
Du kan få tag på undantagsobjekten genom att tilldela dem i except...
del av undantagshanteringskoden:
>>> try:
... failing_function()
... except ValueError as e:
... print('Caught exception', repr(e))
Caught exception ValueError('Example error!',)
En fullständig lista över inbyggda Python-undantag tillsammans med deras beskrivningar finns i Python-dokumentationen: https://docs.python.org/3.5/library/exceptions.html . Och här är hela listan arrangerad hierarkiskt: Undantagshierarki .
Skapa anpassade undantagstyper
Skapa en klass som ärver från Exception
:
class FooException(Exception):
pass
try:
raise FooException("insert description here")
except FooException:
print("A FooException was raised.")
eller annan undantagstyp:
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))
Fånga inte allt!
Det är ofta frestande att fånga varje Exception
:
try:
very_difficult_function()
except Exception:
# log / try to reconnect / exit gratiously
finally:
print "The END"
# it runs no matter what execute.
Eller till och med allt (som inkluderar BaseException
och alla dess barn inklusive Exception
):
try:
even_more_difficult_function()
except:
pass # do whatever needed
I de flesta fall är det dålig praxis. Det kan komma att fånga mer än avsedd, till exempel SystemExit
, KeyboardInterrupt
och MemoryError
- som båda i allmänhet ska hanteras annorlunda än vanliga system- eller logikfel. Det betyder också att det inte finns någon tydlig förståelse för vad den interna koden kan göra fel och hur man kan återhämta sig korrekt från det villkoret. Om du får alla fel, vet du inte vilket fel som inträffade eller hur du åtgärdar det.
Detta kallas oftare 'bug masking' och bör undvikas. Låt ditt program krascha istället för att tyst misslyckas eller ännu värre, och misslyckas med djupare utförande. (Föreställ dig att det är ett transaktionssystem)
Vanligtvis används dessa konstruktioner på programmets yttre nivå och kommer att logga in felets detaljer så att felet kan åtgärdas, eller så kan felet hanteras mer specifikt.
Fånga flera undantag
Det finns några sätt att fånga flera undantag .
Den första är genom att skapa en tupel av de undantagstyper du vill fånga och hantera på samma sätt. Detta exempel gör att koden ignorerar KeyError
och 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.")
Om du vill hantera olika undantag på olika sätt kan du tillhandahålla ett separat undantagsblock för varje typ. I det här exemplet fångar vi fortfarande KeyError
och AttributeError
, men hanterar undantagen på olika sätt.
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)
Praktiska exempel på undantagshantering
Användarinmatning
Föreställ dig att du vill att en användare ska ange ett nummer via input
. Du vill se till att ingången är ett nummer. Du kan använda try
/ except
detta:
while True:
try:
nb = int(input('Enter a number: '))
break
except ValueError:
print('This is not a number, try again.')
Obs: Python 2.x skulle använda raw_input
istället; funktionen input
finns i Python 2.x men har olika semantik. I exemplet ovan skulle input
också acceptera uttryck som 2 + 2
som utvärderar till ett nummer.
Om ingången inte kunde konverteras till ett heltal, ValueError
en ValueError
. Du kan fånga den med except
. Om inget undantag break
hoppar hopp ur slingan. Efter slingan innehåller nb
ett heltal.
ordböcker
Föreställ dig att du itererar över en lista med heltal i följd, som range(n)
, och du har en lista över ordböcker d
som innehåller information om saker att göra när du stöter på vissa heltal, säg hoppa över d[i]
nästa .
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
En KeyError
höjs när du försöker få ett värde från en ordlista för en nyckel som inte finns.
Annan
Koden i ett annat block kommer endast att köras om inga undantag har tagits upp av koden i try
. Detta är användbart om du har någon kod du inte vill köra om ett undantag kastas, men du inte vill att undantag som kastas med den koden ska fångas.
Till exempel:
try:
data = {1: 'one', 2: 'two'}
print(data[1])
except KeyError as e:
print('key not found')
else:
raise ValueError()
# Output: one
# Output: ValueError
Observera att den här typen av else:
inte kan kombineras med en if
startar annat-klausulen till en elif
. Om du har följande if
det behöver förbli indraget under det else:
:
try:
...
except ...:
...
else:
if ...:
...
elif ...:
...
else:
...