How to Pass a Function as a Parameter in Python

To pass a function as a parameter in Python, write its name without parentheses, like checkout(59.99, add_sales_tax). The receiving function stores it in a parameter and calls it whenever it’s ready. Functions are plain objects, so no special syntax is needed:

def checkout(price, rule):
    return rule(price)

checkout(59.99, add_sales_tax)   # no () after add_sales_tax

I tested this page on Python 3.12.5 from a Windows Command Prompt; each output block is pasted straight from that session.

We’ll cover callbacks, key=, lambdas, functools.partial, argument forwarding and type hints.

How do you pass a function as a parameter in Python?

Two price rules, one checkout() that accepts either, and the classic mistake at the end:

def add_sales_tax(price):
    return round(price * 1.0825, 2)

def apply_coupon(price):
    return round(price - 10, 2)

def checkout(price, rule):
    return rule(price)        # call whatever function was passed in

print(checkout(59.99, add_sales_tax))
print(checkout(59.99, apply_coupon))

try:
    checkout(59.99, add_sales_tax(59.99))   # called too early: passes a float
except TypeError as err:
    print("TypeError:", err)

Output:

64.94
49.99
TypeError: 'float' object is not callable
Command Prompt output of Python passing two different functions as a parameter to a checkout function
The same checkout() gives a different total for each rule passed in, and calling the rule too early raises a TypeError.

With parentheses, Python passes the float result, which can’t be called. If defining functions is still new to you, start with how to define a function in Python.

Pass a callback function to run later

A callback is a function another function calls when something happens. This shipping routine just calls whatever you passed:

def ship_orders(orders, on_shipped, on_failed):
    for order_id, state in orders:
        if state in {"TX", "CA", "NY"}:
            on_shipped(order_id)
        else:
            on_failed(order_id, f"no warehouse serves {state}")

def email_customer(order_id):
    print(f"Order {order_id}: shipping email sent")

def log_problem(order_id, reason):
    print(f"Order {order_id}: FAILED - {reason}")

ship_orders([(1001, "TX"), (1002, "AK"), (1003, "NY")], email_customer, log_problem)

Output:

Order 1001: shipping email sent
Order 1002: FAILED - no warehouse serves AK
Order 1003: shipping email sent
Command Prompt output of Python callback functions reporting shipped and failed orders
Orders to Texas and New York trigger the email callback; the Alaska order triggers the failure callback with a reason.

Swap in a Slack message; ship_orders() never changes. If you need to pick the function from a string, such as a menu choice, see calling a function by its string name in Python.

Pass a function to key= in sorted(), max() and min()

Every key= argument is a passed function: Python calls it once per item and compares the results. That’s how many Python built-in functions let you customize their behavior:

customers = [("Olivia", 1250.50), ("james", 980.00), ("Emma", 2310.75)]

def total_spent(customer):
    return customer[1]

print(max(customers, key=total_spent))

names = [name for name, _ in customers]
print(sorted(names))                  # uppercase sorts before lowercase
print(sorted(names, key=str.lower))   # a built-in method works as a key too

Output:

('Emma', 2310.75)
['Emma', 'Olivia', 'james']
['Emma', 'james', 'Olivia']

Pass a lambda function as an argument

When the function is a one-liner you’ll use once, a lambda saves you a named def. Lambdas are covered in depth in how to use lambda functions in Python:

prices = [19.99, 45.00, 120.50, 8.75]

print(list(map(lambda p: round(p * 0.9, 2), prices)))   # 10% off

Output:

[17.99, 40.5, 108.45, 7.88]

map() takes the function first and the data second. My guide to the map() function in Python shows when a list comprehension reads better.

Pass a function with fixed arguments using functools.partial

map() calls its function with one argument, but add_sales_tax() needs two. functools.partial fixes the tax rate ahead of time and gives you a new one-argument callable:

from functools import partial

def add_sales_tax(price, rate):
    return round(price * (1 + rate), 2)

texas = partial(add_sales_tax, rate=0.0625)
california = partial(add_sales_tax, rate=0.0725)

cart = [25.00, 99.99, 14.50]
print("TX:", list(map(texas, cart)))
print("CA:", list(map(california, cart)))
print(texas.func.__name__, texas.keywords)

Output:

TX: [26.56, 106.24, 15.41]
CA: [26.81, 107.24, 15.55]
add_sales_tax {'rate': 0.0625}
Command Prompt output of Python functools.partial fixing the sales tax rate for Texas and California
Each partial object applies its own state rate to every price in the cart.

A lambda like lambda p: add_sales_tax(p, 0.0625) works too, but I prefer partial because it keeps .func and .keywords for debugging.

Forward arguments to the passed function with *args and **kwargs

Wrappers like loggers and retry helpers don’t know the arguments ahead of time. Collect them with *args and **kwargs, then unpack them into the call. The difference between *args and **kwargs explains both:

def run_logged(func, *args, **kwargs):
    print(f"-> {func.__name__} args={args} kwargs={kwargs}")
    result = func(*args, **kwargs)
    print(f"<- {func.__name__} returned {result!r}")
    return result

def book_flight(origin, destination, seats=1, cabin="economy"):
    return f"{seats} {cabin} seat(s) {origin}->{destination}"

run_logged(book_flight, "JFK", "LAX")
run_logged(book_flight, "ORD", "SFO", seats=2, cabin="business")

Output:

-> book_flight args=('JFK', 'LAX') kwargs={}
<- book_flight returned '1 economy seat(s) JFK->LAX'
-> book_flight args=('ORD', 'SFO') kwargs={'seats': 2, 'cabin': 'business'}
<- book_flight returned '2 business seat(s) ORD->SFO'

Type hint a function parameter with Callable

Callable[[float], float] means “a function that takes one float and returns a float.” Python doesn’t enforce it, but mypy does. I ran mypy, then the script:

from collections.abc import Callable

def apply_rule(prices: list[float], rule: Callable[[float], float]) -> list[float]:
    return [rule(p) for p in prices]

def half_off(price: float) -> float:
    return round(price / 2, 2)

def label(price: float) -> str:
    return f"${price:.2f}"

print(apply_rule([40.00, 15.50], half_off))
print(apply_rule([40.00, 15.50], label))   # wrong kind of function

Output:

price_rules.py:13: error: Argument 2 to "apply_rule" has incompatible type "Callable[[float], str]"; expected "Callable[[float], float]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)
[20.0, 7.75]
['$40.00', '$15.50']
Command Prompt output of mypy flagging a function with the wrong Callable signature, then Python running the script anyway
mypy reports that label() returns str instead of float, while Python still runs both calls.

So hints catch the bug in your editor or CI, not at runtime. To check for a callable while the program runs, use the Python callable() function.

These related tutorials build on it:

Frequently asked questions

How do I pass a function as an argument in Python?

Use its name without parentheses, like checkout(59.99, add_sales_tax), then call the parameter inside.

Why do I get “object is not callable” when passing a function?

You added parentheses, so Python passed the return value instead of the function.

How do I pass a function that needs its own arguments?

Forward them with *args and **kwargs, or bind them with functools.partial or a lambda.

What is a callback function in Python?

A function you pass to another function, which calls it later, such as when an order ships.

How do I type hint a function parameter?

Use Callable[[ArgTypes], ReturnType] from collections.abc, such as Callable[[float], float].

Where is functools.partial documented?

See functools.partial() in the Python docs.