5.25. Handling errors¶
Most runtime problems in Python surface as exceptions – a
named, structured way to report that something went wrong.
ValueError, TypeError, KeyError,
OSError, MemoryError are all examples; each is a
class, and raising one stops the current call and looks for a
handler in the surrounding code.
5.25.1. try / except¶
Wrap a block of code in try to catch any exception it
raises:
try:
n = int(input_text)
except ValueError:
n = 0
If the int(...) conversion fails, control jumps to the
except block instead of propagating the error further. If
input_text was a valid integer string, the except block
is skipped.
A single try can have several except blocks, each
catching a different kind of error:
try:
value = data[key]
except KeyError:
value = None
except TypeError:
value = -1
Python matches in order; the first one whose exception class
fits handles the problem. Catching Exception (the base
class for almost everything) handles any error; reserve that
for the outermost layer of a program where the alternative is a
crash.
Warning
A bare except: (no class after the keyword) also catches
KeyboardInterrupt – the exception the IDE sends when
you press its stop button to interrupt a running script.
A loop wrapped in a bare except: pass will swallow the
interrupt and keep running, leaving no way to stop the script
except a power cycle.
Prefer except Exception: over bare except: when you
genuinely need to catch broadly. KeyboardInterrupt
inherits from BaseException, not Exception, so
except Exception: leaves the stop button working.
5.25.1.1. Inspecting the exception¶
To read the message attached to an exception, name it with
as:
try:
f = open("data.txt")
except OSError as e:
print("could not open file:", e)
The variable bound by as is only valid inside the
except block.
5.25.2. else and finally¶
A try block has two optional extras.
else runs only when the try finished without raising:
try:
value = compute()
except ValueError:
print("bad input")
else:
print("got", value)
Putting “what to do when it worked” in else keeps the
try block narrow – only the line that might fail belongs in
try.
finally runs at the end no matter what – whether the
try succeeded, raised and was handled, or raised and is about
to propagate:
try:
do_work()
finally:
cleanup()
finally always runs. else runs only on the
non-exception path.¶
For most acquire/release patterns, prefer a context manager over a try / finally pair –
the resource itself manages its own cleanup.
5.25.3. Common built-in exceptions¶
A short list of the exceptions you will run into often:
ValueError– right type, wrong value (bytes([300])– 300 is the right type, but is outside the valid byte range of 0..255).TypeError– wrong type altogether (len(42)).IndexError– index past the end of a sequence.AttributeError– accessing an attribute that does not exist ("abc".foo).OSError– a filesystem or I/O failure.MemoryError– ran out of heap. On a memory-constrained runtime, this can happen during normal operation – not just in pathological cases.