Python Functions and Methods: A Topic-by-Topic Reference · Lesson 12 of 12

Lesson 12: Errors, Type Tools and Handy Extras

try/except/else/finally, raise, custom errors, getattr, hasattr, callable, isinstance, id, dir, copy, dataclasses and timing code.

Lesson 12: Errors, Type Tools and Handy Extras

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
ErrorUsually means
ValueErrorright type, wrong value (int("x"))
TypeErrorwrong type for the operation (1 + "a")
IndexError / KeyErrorno such position / key
AttributeErrorthe object has no such attribute or method
NameErrora name that was never defined
FileNotFoundErrorthe 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.

Finished this lesson?Log in to save your progress and pick up where you left off.
Back to the course

Comments

Loading comments…

MeroSir Team
A free MeroSir course. Run the examples yourself and try the exercises: you learn programming by doing. Spotted a mistake? Use the Report button above the lesson.
Lesson 12: Errors, Type Tools and Handy Extras | MeroSir