Home / Decorators / Chapter 1

Chapter 1: Foundation

Jun 16, 2025
1 of 7

Before diving into decorators, it’s crucial to understand a core concept in Python: functions are first-class objects. This means they can be treated like any other variable.

  • Assigned to variables: You can assign a function to a variable and then use that variable to call the function.
  • Passed as arguments: Functions can be passed as arguments to other functions.
  • Returned from functions: Functions can return other functions.

Let’s look at a few examples:

Imagine you have simple arithmetic functions and a generic function to apply them:

from typing import Callable # Make sure to import Callable

def multiply(x: float | int, y: float | int) -> float | int:
  return x * y

def add(x: float | int, y: float | int) -> float | int:
  return x + y

def apply(func: Callable[[float | int, float | int], float | int], x: float | int, y: float | int) -> float | int:
  return func(x, y)

Passing functions to other functions:

# Using the apply function
print(apply(add, 2, 3))       # Output: 5
print(apply(multiply, 2, 3))  # Output: 6

Assigning Functions to Variables:

op = multiply
print(op)       # Output: <function multiply at 0x...> (address will vary)
print(op(2, 3)) # Output: 6

Returning Functions from Functions:


def choose_function_to_apply(name: str) -> Callable[[float | int, float | int], float | int]:
  if name == "add":
    return add
  else:
    return multiply

choice = choose_function_to_apply("add")
print(choice)         # Output: <function add at 0x...>
print(choice(2, 7))   # Output: 9

# You can even call it directly
result = choose_function_to_apply("multiply")(2, 7)
print(result)       # Output: 14

Understanding this flexibility is key to grasping how decorators work.