Functions as arguments is a core expression of Python’s first-class function support — you can pass functions directly to other functions, enabling higher-order functions, callbacks, decorators, strategy patterns, dynamic behavior, and composable code. This pattern eliminates long if-elif chains, makes logic injectable/configurable, and promotes reuse and testability. In 2026, passing functions as arguments powers clean, flexible Python — from data transformations in pandas/Polars, event handlers in GUIs/async, ML pipelines with custom loss/metrics, to plugin systems and configurable workflows in production code.
Here’s a complete, practical guide to functions as arguments in Python: passing references, higher-order functions, callbacks, strategies, real-world patterns, and modern best practices with type hints, functools.partial, decorators, and pandas/Polars integration.
Basic passing — reference a function (name without parentheses) and pass it as an argument; the receiving function can call it like any callable.
def add(x: int, y: int) -> int:
return x + y
def apply_operation(a: int, b: int, operation) -> int:
return operation(a, b)
result = apply_operation(4, 2, add)
print(result) # 6
Higher-order functions — take functions as input, return functions, or both; enables abstraction over behavior.
def do_twice(func, x: int, y: int):
return func(func(x, y), func(x, y))
print(do_twice(add, 2, 3)) # add(add(2,3), add(2,3)) ? add(5,5) ? 10
# With lambda for quick inline functions
print(do_twice(lambda a, b: a * b, 2, 3)) # 36
Real-world pattern: dynamic transformations in pandas/Polars — pass functions to apply, map, or aggregate based on config or column type.
import pandas as pd
def normalize(col): return (col - col.mean()) / col.std()
def log_transform(col): return np.log1p(col)
df = pd.DataFrame({'sales': [100, 200, 300], 'visits': [10, 20, 30]})
# Pass functions dynamically
transformations = {
'sales': normalize,
'visits': log_transform
}
for col, func in transformations.items():
df[f'{col}_norm'] = df[col].transform(func)
print(df)
Best practices make function-as-argument usage safe, readable, and performant. Use type hints — Callable[[int, int], int] — signals expectation and helps mypy. Prefer named functions over lambdas for complex logic — easier to debug/test/document. Modern tip: use Polars expressions or pandas method chaining — DRY pipelines with function dispatch. Use functools.partial — pre-bind arguments for reusable partial functions. Use decorators — wrap functions for logging/timing/validation before passing. Pass functions in dicts for strategy pattern — dynamic dispatch. Avoid excessive nesting — deep closures can hurt readability/performance. Test passed functions independently — pure functions are easiest. Combine with abc.ABC or typing.Protocol — define callable interfaces for type safety. Use __call__ in classes — make objects callable like functions for advanced strategies. Use map(), filter(), reduce() — classic functional patterns with passed functions.
Functions as arguments enable higher-order programming — pass behavior as data for dynamic, configurable, reusable code. In 2026, use type hints, prefer named functions, leverage partials/decorators, strategy dicts, and Polars/pandas functional patterns. Master functions as arguments, and you’ll write flexible, composable, testable Python code that adapts elegantly to new requirements.
Next time you need configurable or dynamic behavior — pass a function. It’s Python’s cleanest way to say: “Here’s some logic — apply it how you need.”