Definitions - nested function — a nested function (also called an inner function) is simply a function defined inside another function in Python. The inner function has full access to the variables and scope of its enclosing (outer) function — this creates a closure when the inner function is returned or otherwise escapes the outer scope. Nested functions enable encapsulation, modularity, namespace isolation, private helpers, closures for stateful behavior, and decorators. In 2026, nested functions remain a core Python feature — widely used in factories, decorators, callbacks, scoped transformations in pandas/Polars pipelines, and clean code organization where helper logic should stay local and private. They promote DRY, reduce global pollution, and make code more testable and maintainable when used judiciously.
Here’s a complete, practical guide to nested functions in Python: definition and scope rules, closures and variable access, factory pattern, real-world patterns, and modern best practices with type hints, nonlocal, performance, testing, and pandas/Polars integration.
Basic nested function — inner defined inside outer, can access outer variables (read by default, write with nonlocal).
def outer_function(x: int):
"""Outer function with parameter x."""
captured = x * 2 # local to outer
def inner_function(y: int) -> int:
"""Inner function accesses outer's x and captured."""
return x + captured + y # closure over x and captured
return inner_function
add_ten = outer_function(5) # returns inner with x=5, captured=10
print(add_ten(3)) # 18 (5 + 10 + 3)
Closures — inner function retains reference to outer scope variables even after outer returns.
def make_counter(start: int = 0):
count = start
def counter() -> int:
nonlocal count # required to modify outer variable
count += 1
return count
return counter
c1 = make_counter() # count starts at 0
c2 = make_counter(100) # separate closure, starts at 100
print(c1()) # 1
print(c1()) # 2
print(c2()) # 101
print(c2()) # 102
Real-world pattern: nested functions in pandas/Polars pipelines — create scoped, reusable helpers for cleaning, normalization, or validation without polluting global namespace.
import pandas as pd
def process_dataframe(df: pd.DataFrame, threshold: float):
"""Outer: process df with given threshold."""
def normalize_column(col):
"""Inner: normalize series to zero mean/unit variance."""
return (col - col.mean()) / col.std()
def clip_outliers(col):
"""Inner: clip values outside threshold."""
return col.clip(lower=threshold, upper=1-threshold)
# Apply nested helpers
df = df.apply(normalize_column)
df = df.apply(clip_outliers)
return df
# Usage
data = pd.DataFrame({'A': [1, 2, 100, 4], 'B': [10, 20, 30, 40]})
cleaned = process_dataframe(data, 0.05)
print(cleaned.head())
Best practices make nested functions safe, readable, and performant. Use nested functions for private helpers — keep them local, avoid global namespace pollution. Prefer closures for stateful callbacks — make_counter, event handlers. Use nonlocal to modify outer variables — required in Python 3+. Modern tip: use Polars for lazy pipelines — nested expressions or custom functions for scoped transformations. Add type hints — def outer(x: int) -> Callable[[int], int] — improves clarity and mypy checks. Keep nesting shallow — deep nesting hurts readability; extract to separate functions if complex. Use @functools.lru_cache on returned functions — memoize expensive inner computations. Test nested functions independently — extract to top-level for testing if needed. Combine with decorators — wrap outer or inner for logging/timing. Avoid mutable defaults in nested functions — same pitfalls as top-level. Use contextlib.contextmanager with yield — nested context managers for resource handling.
Nested functions encapsulate local logic, create closures, and enable factories and dynamic behavior. In 2026, use them for scoped helpers, stateful callbacks, type hints, nonlocal, Polars expressions, and shallow nesting. Master nested functions, and you’ll write modular, testable, memory-safe Python code that keeps concerns cleanly separated.
Next time you need a private helper or customized function — define it inside. It’s Python’s cleanest way to say: “This function belongs only here — and it remembers its surroundings.”