Programs rarely fail gracefully; they crash. When Python hits a wall, it raises an exception. Think of this as your program waving its hands and shouting for help instead of silently producing garbage data.
The Mental Model
An exception is like a waiter dropping a tray in a busy restaurant. The kitchen doesn't just keep cooking; it pauses, assesses the mess, and decides whether to clear it or call for help. Your code needs that same pause-and-recover mechanism.
Why It Matters
You will use this constantly when reading user input, parsing JSON config files, or connecting to databases. If a sensor sends "NaN" instead of a number, your program shouldn't die; it should log the error and keep running.
Reading the Traceback
def divide(a, b):
return a / b
def main():
result = divide(10, 0)
print(result)
main()When this runs, Python prints a traceback. Read it bottom-up. The last line names the error type (
ZeroDivisionError) and message. The lines above show exactly where you were when things broke.Catching Exceptions
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
print('Cannot divide by zero')
return None
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # Cannot divide by zero, then NoneThe
try block holds risky code. The except block catches only that specific exception. If the division succeeds, Python skips the except. This keeps your program alive.else and finally
def process(data):
try:
value = int(data)
except ValueError:
print('Invalid number')
else:
print(f'Got: {value}')
finally:
print('Done processing')
process('42')else runs only if the try block succeeds without raising an error. It is perfect for code that depends on a successful operation. finally always runs, making it ideal for closing files or releasing locks.