Handling errors
try / except / else / finally
except runs when an error happens, else when none did, and finally always.
def divide(a, b):
try:
result = a / b
except ZeroDivisionError:
return "cannot divide by zero"
except TypeError as e:
return f"bad types: {e}"
else:
return result
finally:
print("done")
print(divide(10, 4))
print(divide(1, 0))
print(divide("a", 2))
Output:
done
2.5
done
cannot divide by zero
done
bad types: unsupported operand type(s) for /: 'str' and 'int'
raise
Stop with an error of your own, to refuse bad input early.
def set_age(age):
if age < 0:
raise ValueError("age cannot be negative")
return age
try:
set_age(-5)
except ValueError as e:
print("Caught:", e)
Output:
Caught: age cannot be negative
Custom exceptions
Make your own error types by extending Exception.
class InsufficientFunds(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFunds(f"short by {amount - balance}")
return balance - amount
try:
withdraw(100, 150)
except InsufficientFunds as e:
print(e)
Output:
short by 50
assert
A sanity check while developing: stop if a condition is false.
try:
assert 2 + 2 == 5, "maths is broken"
except AssertionError as e:
print("failed:", e)
Output:
failed: maths is broken
Common error types
Learn to recognise these names in error messages.
for bad in ["int(\"x\")", "[1][5]", "{}[\"k\"]", "None.x", "undefined_name"]:
try:
eval(bad)
except Exception as e:
print(type(e).__name__, "->", bad)
Output:
ValueError -> int("x")
IndexError -> [1][5]
KeyError -> {}["k"]
AttributeError -> None.x
NameError -> undefined_name
| Error | Usually means |
|---|---|
ValueError | right type, wrong value (int("x")) |
TypeError | wrong type for the operation (1 + "a") |
IndexError / KeyError | no such position / key |
AttributeError | the object has no such attribute or method |
NameError | a name that was never defined |
FileNotFoundError | the file or folder does not exist |
Looking at objects
hasattr, getattr, setattr
Check for, read, and set an attribute by name.
class Student:
pass
s = Student()
setattr(s, "name", "Sita")
print(hasattr(s, "name"), hasattr(s, "age"))
print(getattr(s, "name"), getattr(s, "age", "unknown"))
Output:
True False
Sita unknown
callable(x), id(x), hash(x)
Is it callable like a function? Its identity? Its hash value (used by dictionaries and sets)?
print(callable(len), callable(5))
a = [1]
b = a
print(a is b, a is a.copy(), a == a.copy())
print(hash("x") == hash("x"))
Output:
True False
True False True
True
dir() and vars()
List the names and attributes of an object. Great for exploring.
print([n for n in dir("") if not n.startswith("_")][:6])
class P:
def __init__(self):
self.x = 1
print(vars(P()))
Output:
['capitalize', 'casefold', 'center', 'count', 'encode', 'endswith']
{'x': 1}
help(x) and x.__doc__
Built-in documentation for any function. In a terminal, help(len) shows it nicely.
print(len.__doc__)
Output:
Return the number of items in a container.
copy.copy and copy.deepcopy
A shallow copy shares inner objects; a deep copy duplicates everything.
import copy
matrix = [[1, 2], [3, 4]]
shallow = copy.copy(matrix)
deep = copy.deepcopy(matrix)
matrix[0][0] = 99
print(shallow[0][0], deep[0][0])
Output:
99 1
Small tools worth knowing
dataclass
Less typing for classes that just hold data.
from dataclasses import dataclass
@dataclass
class Book:
title: str
pages: int = 100
b = Book("Python", 350)
print(b)
print(b == Book("Python", 350))
Output:
Book(title='Python', pages=350)
True
Timing code: time.perf_counter
Measure how long something takes. The number changes each run, so the example is shown but not run.
import time
start = time.perf_counter()
sum(range(1_000_000))
print(f"took {time.perf_counter() - start:.4f} seconds")
sys.argv and __name__
Command-line arguments, and the standard "run only when started directly" guard.
import sys
def main():
print("arguments:", sys.argv[1:])
if __name__ == "__main__":
main()
Output:
arguments: []
Type hints
Optional labels that document and check types with tools such as mypy. Python ignores them when running.
def total(prices: list[float], tax: float = 0.13) -> float:
return sum(prices) * (1 + tax)
print(round(total([100.0, 50.0]), 2))
Output:
169.5
Where to go next
- Practise with the Python: 20 Interview Programs course to apply these tools to classic problems.
- Read the official Python documentation: the standard library index lists every module.
- Build small projects: a to-do app, a CSV report, a web scraper, a simple game.
You have finished the reference course. Keep it bookmarked: it is built to be searched and reused.

Comments
Loading comments…