Referencing a function in Python means using the function's name without parentheses — this gives you a reference to the function object itself, not the result of calling it. Because functions are first-class objects, this reference can be assigned to variables, passed as arguments, returned from other functions, stored in lists/dicts, or used anywhere a callable is expected. Referencing (not calling) is the key to higher-order functions, callbacks, decorators, strategy patterns, dynamic dispatch, and configurable code. In 2026, this technique powers clean, flexible, reusable Python — from decorators and event handlers to pandas/Polars custom transforms and plugin systems.
Here’s a complete, practical guide to referencing functions in Python: syntax and mechanics, assignment, passing as arguments, returning functions, storing in structures, real-world patterns, and modern best practices with type hints, closures, functools, and pandas/Polars integration.
Referencing vs calling — name alone is reference; parentheses invoke the function.
def add(a: int, b: int) -> int:
return a + b
# Reference — add is the function object
operation = add
print(operation) #
# Call via reference
result = operation(4, 2) # 6
print(result)
Passing referenced functions as arguments — enables callbacks, higher-order functions, strategies.
def apply_operation(x: int, y: int, operation) -> int:
return operation(x, y)
def subtract(a: int, b: int) -> int:
return a - b
result_add = apply_operation(10, 5, add) # 15
result_sub = apply_operation(10, 5, subtract) # 5
print(result_add, result_sub)
Returning referenced functions — factory pattern, closures, customizable behavior.
def make_adder(increment: int):
def adder(n: int) -> int:
return n + increment
return adder # return reference to inner function
add5 = make_adder(5)
add10 = make_adder(10)
print(add5(3)) # 8
print(add10(3)) # 13
Storing references in lists/dicts — dynamic dispatch, plugin systems, configurable operations.
math_ops = {
"add": add,
"subtract": subtract,
"multiply": lambda x, y: x * y
}
def compute(x: int, y: int, op_name: str) -> int:
func = math_ops.get(op_name)
if func is None:
raise ValueError(f"Unknown operation: {op_name}")
return func(x, y)
print(compute(10, 5, "add")) # 15
print(compute(10, 5, "multiply")) # 50
Real-world pattern: dynamic transformations in pandas/Polars — reference functions in dict, apply 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)
transformations = {
"sales": normalize,
"visits": log_transform
}
df = pd.DataFrame({
'sales': [100, 200, 300],
'visits': [10, 20, 30]
})
for col, func in transformations.items():
df[f"{col}_transformed"] = func(df[col])
print(df)
Best practices make function referencing safe, readable, and performant. Use type hints — Callable[[int, int], int] or dict[str, Callable[[int, int], int]] — improves clarity and mypy checks. 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 closures for stateful functions — factory pattern. Use functools.partial — pre-bind arguments for reusable partials. Use decorators — wrap referenced functions for logging/timing/validation. Store in dicts for strategy pattern — dynamic dispatch. Avoid excessive nesting — deep closures can hurt readability/performance. Test referenced 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.
Referencing a function (name without parentheses) gives you the function object — assign, pass, return, store, and invoke it dynamically. In 2026, use type hints, prefer named functions, leverage partials/decorators, strategy dicts, and Polars/pandas functional patterns. Master referencing functions, and you’ll write flexible, composable, testable Python code that adapts without rewriting logic.
Next time you need dynamic or configurable behavior — reference a function. It’s Python’s cleanest way to say: “Here’s the function — use it however you need.”