Skip to main content
Python’s multi-paradigm nature lets you treat functions as first-class citizens: you can store them in variables, pass them into other functions, return them as values, and inspect their attributes. This goes far beyond simple code reuse — it unlocks powerful patterns like closures (functions that remember their creation environment) and decorators (wrappers that extend behaviour without touching the original code). This page covers every advanced function concept you need before diving into FastAPI and modern Python libraries.

Functions as First-Class Objects

In Python, a function is an object in memory with a type, an identity, and a value. You can manipulate it like any other value:

Lambda Functions

A lambda is a small, anonymous function defined with a single expression. No return keyword is needed — the expression is evaluated and returned automatically.

Real-World Lambda Example

Multi-line Lambda Expressions (Nested Conditionals)

You can wrap a single expression across multiple lines using parentheses:
Complex nested conditionals inside a lambda hurt readability. If your logic spans multiple lines or requires multiple statements (assignments, loops, print calls), use a normal def function.
Lambdas shine as concise inline arguments to higher-order functions:

Variable-Length Arguments (*args and **kwargs)

Accept an arbitrary number of arguments using starred parameters:
  • *args collects extra positional arguments into a tuple.
  • **kwargs collects extra keyword arguments into a dictionary.

Closures

A closure is a nested function that retains access to variables from its enclosing function’s scope — even after the outer function has finished executing. Three conditions must hold:
  1. There is a nested (inner) function.
  2. The inner function references a variable from the outer scope.
  3. The outer function returns the inner function.
Each call to make_multiplier() creates an independent closure with its own factor value — double and triple are completely separate functions.

Decorators

A decorator is a higher-order function that wraps another function to add behaviour before, after, or around the original call — without modifying its source code.

Writing a Basic Decorator

The @my_decorator syntax is shorthand for say_hello = my_decorator(say_hello).

Decorating Functions with Arguments

Use *args and **kwargs in the wrapper so the decorator works with any function signature:

Practical Decorator Example — Timing

For production decorators, wrap the inner function with functools.wraps(func) to preserve the original function’s __name__, __doc__, and other metadata: