
A Python function sometimes needs to let another function choose how to handle a value. Passing the function by name gives the receiving function a callable it can invoke when needed.
I’ll explain when to pass a function rather than call it, then show how to pass one with arguments already set.
TL;DR
Pass the function object by name, without parentheses, and let the receiving function call it when needed.
transform refers to a function. transform(value) calls it and passes its result.
A callback parameter is an ordinary parameter whose value is callable.
Use lambda or functools.partial when the callback needs preset arguments.
What Does It Mean to Pass a Function?
A Python function is an object you can pass around like other values. A higher-order function is a function that accepts another callable, and the supplied callable is often called a callback.
| Expression | Meaning |
|---|---|
| clean_text | Function object, ready to pass |
| clean_text(" Hi ") | Call now, yielding a return value |
| callback | Variable that may refer to a callable |
How to Pass a Function as an Argument in Python
The example uses lowercase to show how the arguments move through apply_to_text. The function returns the transformed string, and the script prints it.
Step 1: Define the callback and receiver
The callback below accepts one string. apply_to_text also receives the text to process.
def lowercase(text):
return text.lower()
def apply_to_text(function, text):
return function(text)
result = apply_to_text(lowercase, "AskPython")
print(result)
Running python3 callback_demo.py prints this exact result:
askpython

The output is lowercase because lowercase returns text.lower().
Step 2: Pass the function object
apply_to_text takes a transform parameter and a text value, so its caller can choose lowercase or another operation without changing the batch flow. Keeping print outside the receiver lets another caller reuse its returned string.
Step 3: Pass a bound method or lambda
A lambda is useful for a short expression that does not need a separate definition. Its body is one expression, so use def when the callback needs multiple steps, validation or a reusable name.
values = ["pear", "Apple", "plum"] sorted_values = sorted(values, key=str.lower) squares = list(map(lambda number: number * number, [1, 2, 3]))
str.lower is an unbound method descriptor. Retrieved from a string instance as word.lower, it becomes a bound method.
sorted calls key once for each input item, compares the returned lowercase strings, and returns the original items in that order. The returned list keeps the original strings. The key must produce values that can be compared with each other.
In this example, map supplies one value from each input iterable on each call.
map returns an iterator and computes values as a consumer requests them. Convert it to a list when later code must inspect or reuse all results.
With multiple input iterables, map stops at the shortest input and does not process remaining values in longer ones. In Python 3.14 and later, strict=True raises ValueError when the input lengths differ.
Use itertools.zip_longest when you need to process every value from uneven inputs. It pads missing values with fillvalue, so choose a value the callback can handle. Limit the iteration if any input can be infinite.
A list comprehension evaluates its expression for each input value and produces a list directly. It can include an if clause to select values in the same pass, while map is useful when you already have a callback to reuse.
Python sorting is stable, so strings with equal lowercase keys keep their input order. That matters when the input is already ordered by another field and you sort it again using a case-insensitive key.
Use filter when you need to retain items that meet a condition: a truthy predicate result keeps the original item, while a false result drops it. Passing None uses each value’s truthiness directly, which removes empty strings, zero, and None, so use a custom predicate when zero is valid.
The two-argument form of iter repeatedly calls a zero-argument function until it returns the sentinel. Use partial to bind a chunk size to file.read, and choose an empty string or empty bytes sentinel to match the stream type. The next chunk is read only as the consumer advances, which avoids loading the whole file into memory at once.
Step 4: Bind arguments before passing
The example calculates tax from a changing price and a fixed rate.
from functools import partial
def add_tax(price, rate):
return price * (1 + rate)
with_tax = partial(add_tax, rate=0.08)
print(with_tax(100))
prices = [100, 200]
print(list(map(lambda price: add_tax(price, 0.08), prices)))
partial binds rate by name and leaves price for map to supply. Its result is callable, positional arguments are prepended, and call-time keywords can override bound keywords.
A forwarding wrapper can preserve the caller’s variable positional and named arguments, which is useful when adding logging around another function. Python collects extra positional arguments in a tuple and extra named arguments in a dictionary. Prefer explicit parameters when the receiver has a fixed callback contract that should be straightforward to verify.
A callback passed to map receives each item as its positional argument. Optional parameters can use defaults, but a keyword-only setting needs an adapter when map cannot supply it by name.
Python evaluates defaults when it defines the function. A mutable default can therefore be shared across callback calls, so use None and create a fresh object inside the function when each call needs separate state.
An instance with a __call__ method can hold configuration or state for a callback that needs more than its input value. For example, a validator object can keep a threshold between calls while exposing one callable operation.
Callable[[str], str] from collections.abc describes a string-in, string-out callback for static tools, while a Protocol with __call__ can express keyword-only parameters that Callable cannot. Python does not enforce either form at runtime, so test the receiver with representative callbacks.
What Can Go Wrong When Passing a Function?
Common callback failures include TypeError and an object-is-not-callable error.
| Case | What happens | Adjustment |
|---|---|---|
| Pass callback() | The function runs immediately and its result is passed. | Pass callback when the receiver expects a callable. |
| Callback expects two inputs, receiver gives one | A call can raise TypeError. | Bind an input with lambda or partial, or change the receiver. |
| Use a lambda that references a changing loop variable | Calls can observe the variable’s later value. | Bind the current value in a default parameter, such as lambda item, n=n: …. |
| Call reduce on empty data without an initial value | There is no first item to seed the reduction. | Supply an initial value or use an operation with a defined empty result. |
callable(value) only checks whether an object can be called, not whether a particular call will work or what it returns. Test with a small callback that records the arguments supplied by the receiver and returns a known value.
For multiplication, functools.reduce passes the accumulated value and next item, in that order, to operator.mul to produce one product from the iterable.
from functools import reduce from operator import mul product = reduce(mul, [2, 3, 4], 1)
The initial value 1 is the multiplication identity, so reduce returns 1 for an empty iterable and starts from 1 when values are present. Without it, reduce needs a first item to seed the accumulator, so an empty iterable raises an error.
For ordinary totals, prefer sum over reduce because the intent is visible without supplying a callback. Choose reduce when the operation combines each value with an accumulated result and no clearer built-in expresses that operation.
reduce returns only the final accumulated value. Use itertools.accumulate when later code needs intermediate totals or products. It returns an iterator and includes the initial value as an extra first result.
If the traceback says an object is not callable, check the value assigned to that name before the failing line. It may hold a return value produced by an earlier function call instead of the function itself.
Exceptions raised inside the callback travel through the calling function unless it catches them. A reusable receiver should catch one only when it can recover or report a useful result. Otherwise, let the traceback identify the failing callback.
Conclusion: Pass the Callable, Not Its Result
When a function expects behavior, pass a function reference without parentheses.
For more details, see the Python documentation for map, sorted, and functools.partial. See also AskPython’s callback functions and map guides.
FAQ
These answers cover common syntax questions about a Python function argument.
How do you pass a function without calling it?
Write the function name without parentheses, such as apply(callback). The receiving function can call callback later.
How can I pass extra arguments to a callback?
Wrap the call in a lambda or use functools.partial to bind arguments before passing the resulting callable.
Can a method be passed as a function argument?
Yes. A bound method such as instance.method carries its instance and can be passed to a function that expects a callable.
How many arguments can a Python function accept?
A function accepts the arguments in its signature; variable positional arguments and named arguments let it receive additional values.
What happens when map receives multiple iterables?
The callback receives one item from each iterable per call, and map stops when the shortest iterable is exhausted.
Does filter return the callback result?
No. filter yields each original item whose predicate result is true, rather than yielding the predicate result itself.
Can a callback use default arguments?
Yes. The receiver can supply required arguments and the callback uses its defaults for optional parameters it does not receive.
When are Python default arguments evaluated?
Python evaluates default arguments when it defines the function, so a mutable default can be shared across calls.
How much can a Python lambda contain?
A lambda body is one expression, so use def when a callback needs multiple statements or a reusable name.
What do variable positional and named arguments collect?
Extra positional arguments are collected into a tuple, and extra named arguments are collected into a dictionary.
How do reduce and accumulate differ?
reduce returns the final accumulated value, while itertools.accumulate yields intermediate accumulated values as an iterator.
How does iter stop when given a callable and sentinel?
It calls the zero-argument function repeatedly and stops when the function returns the sentinel value.
How can I map uneven inputs without dropping values?
Use itertools.zip_longest to pad shorter inputs with a fill value so iteration continues through the longest input.
Sanjay Kumar
NITJ | Beta Microsoft Learn Student Ambassador | Technical Content Writer | Python Developer | Linux Enthusiast


