Learn by reading through in order

Higher-Order Functions — Treating Functions as Arguments and Return Values

Treat Python functions as values — pass them as arguments and return them — all by running examples in your browser.

In the previous article on yield, you saw functions that produce values one at a time. This time you'll keep going on the function side and look at higher-order functions — the mechanism that lets you treat a function itself as a value.

Three patterns to keep in mind: assigning to a variable, passing as an argument (a callback), and returning as a value.

What's a Higher-Order Function — Functions Are Values Too

In Python, functions are values just like integers or strings. You can put them in variables, pass them to other functions as arguments, and return them as results.

A function that takes a function as an argument, or returns a function as a result, is called a higher-order function. Unlike print() or len(), which just operate on values, higher-order functions assemble pieces of behavior.

Three Patterns Higher-Order Functions Cover
①Assign afunction to a varf = printf("HELLO")②Pass afunction as argafter(2, greet)→ callback③Return afunction as valuemake_greeter("Alice")→ returns a function
Because functions are values, you can use them three ways: variable assignment, argument passing, and return value.

Assigning a Function to a Variable — Functions Are Referenced Values Too

When you define a function with def, the function body is built in memory and the function name points to that location. Assign with a = print, and a now points to the same function body, so a("HELLO") and print("HELLO") do exactly the same thing.

The trick is to leave off the () after the function name. Add () and you call the function instead, and the return value gets assigned.

A Function Name Is Just a Name Pointing to a Function Object
print(built-in name)function object(body)OutputHELLOa(your alias)referssame refprint("HELLO")a("HELLO")
a = print              # no () — assigns the function body itself to a
a("HELLO")            # HELLO  ← same as print("HELLO")

print(id(print))       # e.g. 4395020128
print(id(a))           # the same address shows up

# Your own functions work the same way
def greet():
    print("Hello")

say = greet            # build an alias say
say()                  # Hello

With or Without () Changes the Meaning

a = print is the form that passes the function, while a = print("HELLO") is the form that calls the function and passes the result. In the second case, print("HELLO")'s return value (None) lands in a, so calling a() raises TypeError: 'NoneType' object is not callable.

Get a feel for functions as values, with both your own function and a built-in.

① Define def greet(): with a body of print("Hello").

② Build an alias with say = greet and call say() — confirm you get the same result as greet().

③ Alias the built-in len too with f = len, and print the result of f("Python") with print(...).

(When the answer is correct, the explanation will appear.)

Python Editor

Run code to see output

Passing a Function as an Argument — Callbacks

The most typical use of higher-order functions is the callback — "a function passed to another function and called at a specific moment", where "the caller decides the actual behavior".

For instance, say you have a routine that "prints a greeting for a list of three names", but you want to swap only the greeting style from the call site. Keep the body as a loop that pulls names one at a time, and take the formatting piece as a function argument — then the caller can reuse the routine just by changing the greeting template.

def greet_all(names, formatter):
    for name in names:
        print(formatter(name))

def formal(name):
    return f"Dear {name}, thank you as always."

def casual(name):
    return f"Hey, {name}!"

greet_all(["Alice", "Bob", "Carol"], formal)
# Dear Alice, thank you as always.
# Dear Bob, thank you as always.
# Dear Carol, thank you as always.

greet_all(["Alice", "Bob", "Carol"], casual)
# Hey, Alice!
# Hey, Bob!
# Hey, Carol!
A Callback Means "Pass the Inner Behavior In"
Caller sidegreet_all(names, formal)Higher-order fngreet_all(names, formatter)For each name,call formatter(name)Callbackformal(name)"Dear Alice, ..."3 lines of outputpass fnbodyswappableresult
greet_all's body is just a loop; how to turn each name into a greeting line is left to the formatter argument.

Build a higher-order function that lets the caller swap the notification format for a list of usernames.

① Define def notify_all(users, formatter):. With for user in users:, call print(formatter(user)) for each user.

② Build def login_alert(name): that returns f"[Login] {name} signed in".

③ Build def logout_alert(name): that returns f"[Logout] {name} signed out".

④ Set up users = ["Alice", "Bob"], then call notify_all(users, login_alert) and notify_all(users, logout_alert).

Python Editor

Run code to see output

Branching on Purpose-Specific Callbacks

You're not limited to passing a single callback. "Use this function on success, that one on failure" is another natural fit — anywhere you'd want to pick between multiple callbacks.

For example, when you want to switch behavior based on whether a number is even or odd, the deciding function can do nothing but if-branch, and leave the actual processing to two functions provided by the caller.

def process_number(number, even_callback, odd_callback):
    if number % 2 == 0:
        even_callback(number)
    else:
        odd_callback(number)

def handle_even(n):
    print(f"{n} is even")

def handle_odd(n):
    print(f"{n} is odd")

process_number(4, handle_even, handle_odd)   # 4 is even
process_number(7, handle_even, handle_odd)   # 7 is odd
Switching the Callback Based on a Condition
process_number(7,handle_even,handle_odd)n % 2 == 0?even_callback(n)→ handle_evenodd_callback(n)→ handle_odddecideTrueFalse

Build a higher-order function that routes payment processing to one callback on success and another on failure.

① Define def process_payment(amount, on_success, on_failure):. If amount > 0, call on_success(amount); otherwise call on_failure(amount).

② Build def notify_success(amount): that prints f"Payment of {amount} yen completed".

③ Build def notify_failure(amount): that prints f"Amount {amount} is invalid".

④ Call process_payment(1500, notify_success, notify_failure) and process_payment(0, notify_success, notify_failure).

Python Editor

Run code to see output

Returning a Function as a Value — Closures in Practice

The other higher-order pattern is "returning a function as a value". Returning an inner function with return was covered in the closures article; here, the focus is the practical angle of "handing the caller a function that already remembers its setting".

For example, when you want to mass-produce log functions that stamp the same prefix on every call, set up make_logger("INFO") for the INFO logger and make_logger("ERROR") for the ERROR logger — and the calling code stays as short as info("Process started").

def make_logger(prefix):
    def log(message):
        print(f"[{prefix}] {message}")
    return log               # return the function itself

info = make_logger("INFO")
error = make_logger("ERROR")

info("Process started")     # [INFO] Process started
error("Connection failed")  # [ERROR] Connection failed
info("Process finished")    # [INFO] Process finished
make_logger Returns a "Pre-Configured Function"
Module (Global Namespace)
  • info = make_logger("INFO") — receives a function that remembers INFO
  • error = make_logger("ERROR") — receives a separate function that remembers ERROR
make_logger("INFO")'s Frame
  • Holds prefix = "INFO"
  • returns the log defined inside
log (still remembers INFO)
  • Prints [INFO] ... on every call
make_logger("ERROR")'s Frame
  • Holds prefix = "ERROR"
  • returns a separate log function
log (still remembers ERROR)
  • A separate function object, independent from info
Each call to make_logger builds a separate log function that remembers its prefix and returns it. Callers use them under short names like info() / error().

Build a higher-order function that returns a tagging function that remembers its tag.

① Define def make_tagger(tag):. Inside it, define def tag_text(text): that returns f"<{tag}>{text}</{tag}>".

② At the end, return the inner function with return tag_text.

③ Build two functions: b = make_tagger("b") and i = make_tagger("i").

④ Call print(b("Important")) and print(i("Emphasis")) and confirm <b>Important</b> / <i>Emphasis</i> show up respectively.

Python Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1What does this code print?
def greet():
print("Hi")
f = greet
f()

Q2Which line passes say_hi as a callback?

Q3What does info("OK") print after this code runs?
def make_logger(prefix):
def log(message):
print(f"[{prefix}] {message}")
return log
info = make_logger("INFO")