Exception Handling in Python
Raising Exceptions: raise and assert
Python raises exceptions on its own, and a program can raise one deliberately. The raise statement takes the form raise ExceptionName(optional message) — for example raise ValueError("marks cannot be negative"). As soon as raise executes, the rest of the current block is skipped and Python looks for a handler. The assert statement tests a condition the programmer expects to be true: assert expression, message. If the expression is True nothing happens; if it is False, Python raises AssertionError carrying the message. assert is a quick check on input or on an assumption, such as a number being non-negative before its square root is taken.
For example, this fragment prints 13 and then 4 — the line after raise never runs:try: print("1", end="") raise IndexError print("2", end="")except IndexError: print("3", end="")print("4")
Handling Exceptions: try and except
When an error occurs, Python creates an exception object and hands it to the runtime system — this is called throwing the exception. The runtime system then searches backwards through the calls that led to the error for code that can deal with it, the exception handler; finding one is called catching the exception. If no handler is found anywhere, the program stops. Code that might fail is placed in a try block, and the handler in an except block that names the exception it handles. If the try block runs without an error, every except block is skipped.
- Several except clauses may follow one try, one per exception; Python runs the first whose name matches and skips the rest.
- An except with no exception name catches every exception. It must be the last except clause — placing it earlier is itself a SyntaxError ("default 'except:' must be last").
- An unmatched exception — one no except clause names — is not handled here; it travels outwards to the caller, and if nothing catches it the program terminates.
A worked trace. Each item is tried in turn; "x" cannot become an integer and "0" makes the division fail, so the total is 3 + 100 + 2 + 1000 and the program prints 1105:total = 0for item in ["4", "x", "6", "0"]: try: total += 12 // int(item) except ValueError: total += 100 except ZeroDivisionError: total += 1000print(total)
The else and finally Clauses
A try statement can carry two more clauses. The else clause runs only if the try block raised no exception; it is the place for code that should run after the risky part succeeds. The finally clause runs always — whether the try block succeeded, an exception was caught, or an exception was not caught at all — so it is where clean-up goes, such as closing a file. If an exception is not handled, finally still runs first and the exception is then passed on. The order on the page is fixed: try, then the except clauses, then else, then finally.
| What happens in try | except | else | finally |
|---|---|---|---|
| No exception | skipped | runs | runs |
| Exception matched by an except | the matching one runs | skipped | runs |
| Exception matched by none | none runs | skipped | runs, then the exception propagates |
done -1:def f(a, b): try: return a / b except ZeroDivisionError: return -1 finally: print("done", end=" ")print(f(6, 0))Key takeaways
- Syntax errors are caught while the code is parsed, so nothing runs; exceptions arise during execution of syntactically valid code.
- ValueError: right type, wrong value (int("3.5")); TypeError: wrong type ("5" + 5); IndexError, NameError, ZeroDivisionError, EOFError name their cause.
- raise throws an exception deliberately and skips the rest of the block; assert raises AssertionError when its condition is False.
- Python runs the first matching except; a bare except catches everything and must come last.
- else runs only when try raised nothing; finally runs in every case, even after a return, and before an unhandled exception propagates.
- Throwing creates the exception object; catching is the runtime system finding a handler for it.
Practice questions (8)
Attempt each one before opening the answer. Every explanation names the tempting wrong option as well as the right one, because that is where marks are lost.
What is the output of the following code?
try:x = int("12a")print("A", end="")except ValueError:print("B", end="")else:print("C", end="")finally:print("D", end="")Show answer
Answer: B — BD
int("12a") raises ValueError on the first line of try, so print("A") never runs; the matching except prints B, else is skipped because try did not finish cleanly, and finally prints D — BD. ABD forgets that the rest of try is abandoned the moment the exception is raised, and BCD runs else, which happens only when try raises nothing.Which exception is raised by the expression int("3.5") in Python 3?
Show answer
Answer: D — ValueError
int() accepts a string argument, so the type is acceptable; what fails is the value, because "3.5" is not a valid integer literal — that is a ValueError. TypeError is the trap: it would be raised if the type itself were unusable, as in "5" + 5. The line is grammatically valid, so it is not a SyntaxError, and every name in it is defined.In a try statement, when is the else clause executed?
Show answer
Answer: A — Only when the try block completes without raising any exception
else is the success branch: it runs only if the try block finished without an exception, which keeps code that depends on that success out of the try block. "Always" describes finally, not else. When an exception is caught, the except clause runs instead of else, and when none matches, neither except nor else runs — only finally, before the exception moves on.What does the following code print?
def f(a, b):try:return a / bexcept ZeroDivisionError:return -1finally:print("done", end=" ")print(f(6, 0))Show answer
Answer: C — done -1
6 / 0 raises ZeroDivisionError, the except clause reaches return -1, and finally still runs before the function actually returns, printing "done " with a space. Only then does the outer print receive -1 and print it, giving done -1. Writing -1 alone assumes return skips finally, and "-1 done" gets the order backwards, as if the value were printed before the function finished.Consider: x = -3 followed by assert x >= 0, "negative". What happens when the assert statement runs?
Show answer
Answer: C — It raises an AssertionError with the message "negative"
assert evaluates its condition; -3 >= 0 is False, so Python raises AssertionError and attaches the message after the comma. Nothing is printed on its own — if the exception is not handled, the program stops with a traceback ending in AssertionError: negative. ValueError is a different exception that assert never raises, and assert only checks a condition; it never changes a variable.Assertion (A): A Python script with a syntax error on its last line does not print anything, even from the lines above the error. Reason (R): Syntax errors are detected when the code is parsed, before any statement is executed.
Show answer
Answer: A — Both A and R are true, and R is the correct explanation of A
Python reads and parses the whole script before running it, so a syntax error anywhere stops the program from starting; the print statements above the faulty line are never reached. That parsing step is exactly why A happens, so R explains A. Treating A as false confuses syntax errors with exceptions, which do let the earlier lines run because they arise only during execution.A programmer writes a bare except: clause first and an except ValueError: clause after it, under the same try. What happens?
Show answer
Answer: D — Python refuses to run the code: a bare except must be the last clause
A bare except catches every exception, so any clause after it could never be reached; Python treats that ordering as an error in the code itself and reports SyntaxError: default 'except:' must be last before running anything. The idea that the later clause is "simply never reached" is the tempting answer, but Python does not wait to discover that at run time, and only one except clause ever runs for an exception.In exception handling, what is meant by "catching" an exception?
Show answer
Answer: B — The runtime system finding and executing a suitable handler for it
When an error occurs, creating the exception object and handing it to the runtime system is throwing; the runtime system then searches for code that can handle it, and locating and running that handler is catching. Creating the object is throwing, the opposite end of the process. Syntax errors are found during parsing and are not caught by the program at all, and nothing about catching involves a log file.