try and except
An exception interrupts a program that was running. Left alone, Python prints a traceback and stops. Handling it means telling Python in advance what to do instead — and try and except are the two blocks that say it.
1Why bother? Two reasons, and both matter
Compare what a user sees. This is the unhandled version — a normal program, given a zero:
print('--- Marks Calculator ---')
total = 250
subjects = 0
average = total / subjects
print('Average:', average)
print('--- Thank you ---')--- Marks Calculator ---
Traceback (most recent call last):
File "unhandled.py", line 6, in <module>
average = total / subjects
~~~~~~^~~~~~~~~~
ZeroDivisionError: division by zeroSix lines of red text, half of it about Python's own machinery, and the program is dead. The closing message never printed. To anyone who is not a programmer this looks like the computer broke.
print('--- Marks Calculator ---')
total = 250
subjects = 0
try:
average = total / subjects
print('Average:', average)
except ZeroDivisionError:
print('No subjects were entered, so the average cannot be worked out.')
print('--- Thank you ---')--- Marks Calculator --- No subjects were entered, so the average cannot be worked out. --- Thank you ---
The program finishes on its own terms. It says something a person can read and act on, and the lines after it still run — the closing message, the summary, the goodbye. Nothing is left half-done and nobody is shown a traceback they cannot use.
The handler sits around the exact lines that can fail, so the message can name the actual situation — 'no subjects were entered' rather than 'division by zero'. That points at the part of the program to look at, which is most of the work of debugging.
2The shape of it
Both are ordinary blocks — a line ending in a colon, then an indented body, exactly like if, for and def. The except line names the exception it is prepared for.
3What Python actually does
The interesting part is that try behaves differently depending on whether anything goes wrong — so it is worth seeing both runs of the same program.
numerator = 10
denominator = 0
try:
result = numerator / denominator
print('Result:', result)
except ZeroDivisionError:
print('Cannot divide by zero — please check the denominator.')
print('The program carries on.')Cannot divide by zero — please check the denominator. The program carries on.
Notice what is not there: Result: never printed. The moment the division failed, Python abandoned the rest of the try block and jumped to the handler. Now the same program with a 2:
numerator = 10
denominator = 2
try:
result = numerator / denominator
print('Result:', result)
except ZeroDivisionError:
print('Cannot divide by zero — please check the denominator.')
print('The program carries on.')Result: 5.0 The program carries on.
This time the whole try block ran and the except block was skipped entirely. A handler is not a detour; it is a standby.
- 1. Run the
tryblock, line by line. - 2. If nothing goes wrong, skip
exceptcompletely and carry on below. - 3. If an exception is raised, abandon the rest of the
tryblock at once. - 4. If the
exceptnames that exception, run it — and then carry on below as if nothing happened.
try block does not run. Not the next line, not the line after it. That is why you keep a try block short — every line in it is a line you are agreeing to skip.4Getting Python's own message with as
Sometimes you want your friendly sentence and the technical detail — when you are debugging, or writing to a log. as gives the exception a name so you can print it:
try:
marks = int('twelve')
except ValueError as e:
print('Could not read that as a number.')
print('Python said:', e)Could not read that as a number. Python said: invalid literal for int() with base 10: 'twelve'
marks = int('twelve')int() is happy to convert '12'. 'twelve' is a string of the right type holding a value it cannot use — which is exactly what ValueError means.
except ValueError as e:as e puts the exception object into the name e. Any name works; e is the usual one.
print('Python said:', e)Printing the exception prints its message — the same text that would have appeared on the last line of the traceback.
5Wrap the risky lines, not the whole program
It is tempting to put a try around everything and be done with it. Resist. A short try block does two things for you: it says clearly which line you expected to fail, and it means an unexpected failure somewhere else is not quietly reported as the problem you were guarding against.
6Recap
except says what to do if they go wrong. Both are ordinary colon-and-indent blocks.
A readable sentence instead of a traceback, and the lines below still run — so the program finishes properly.
The handler sits around the lines that can fail, so its message can name the real situation. That is where debugging starts.
As soon as an exception is raised, Python jumps to the handler. Nothing else in the try runs.
If the try block finishes cleanly, the except block is skipped entirely.
print(e) shows the same text as the last line of the traceback. Useful while debugging.
- 1
Run the marks calculator with
subjects = 0, then withsubjects = 5.Hint · Same program, two paths. The closing line prints either way.
- 2
Put a second
printafter the division, inside thetry, and check it does not run.Hint · The rest of the block is abandoned the moment it fails.
- 3
Catch a
ValueErrorfromint('twelve')and print the message withas e.Hint · It is the same sentence the traceback would have ended with.
- 4
Wrap a whole program in one
try, then shrink it to the one risky line, and compare how each reads.Hint · The short one tells the reader which line you were worried about.
With denominator = 0, why does 'Result:' never print?
What happens to the except block when nothing goes wrong?
What does handling an exception actually change?