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.

Python 3.x 3.0

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:

Python 2.x 2.3
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
Python 3.x 3.0
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:

Python 3.x 3.0
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:
        ...


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow