Skip to Content
Course content

81: First-Class Functions and Passing Functions as Arguments

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this specific bug a dozen times in code reviews. A developer wants to make their code flexible by passing a function into another function—which is a powerful pattern—but they trip over a tiny syntax detail that changes the entire meaning of the line.

def double(n):
    return n * 2

def apply_operation(numbers, operation):
    return [operation(x) for x in numbers]

nums = [1, 2, 3, 4]
# Oops! Here is where it goes wrong:
result = apply_operation(nums, double()) 
print(result)

The 'TypeError: double() missing 1 required positional argument' Trap

If you run the code above, Python is going to scream at you. You'll get a TypeError saying double() is missing an argument. Why? Because of those parentheses: double().

In Python, when you add parentheses to a function name, you aren't talking about the function; you are executing the function right then and there. In the example above, Python tries to run double() first, and only then tries to pass the result of that call into apply_operation. Since double requires an input n, it crashes before apply_operation even starts.

Even if double didn't require an argument, you'd still have a problem. If double() returned the number 10, you'd essentially be calling apply_operation(nums, 10). Then, inside the list comprehension, Python would try to do 10(x), which would throw a different error: 'int' object is not callable.

Passing the Reference, Not the Result

To fix this, we have to treat the function as a "first-class citizen." In Python, functions are just objects, like strings or lists. You can move them around and pass them into other functions without actually running them yet.

The fix is simple: remove the parentheses.

def double(n):
    return n * 2

def apply_operation(numbers, operation):
    # 'operation' is now a reference to the function we passed in
    return [operation(x) for x in numbers]

nums = [1, 2, 3, 4]
# Pass the function itself, not the result of calling it
result = apply_operation(nums, double) 
print(result) # Output: [2, 4, 6, 8]

By passing double (no parentheses), you're handing a reference to the function over to apply_operation. Now, apply_operation holds that reference in the variable operation and decides when and how to call it.

Why This Actually Matters in Real Projects

You might be thinking, "Why not just write a double_list function?" In a small script, sure. But in a real-world codebase, this is how you build plugins, flexible API wrappers, or custom sorting logic.

Think about Python's built-in sorted() function. It has a key argument. When you pass a function to key, you're telling Python: "Don't just sort these items; first, run every item through this function, and sort them based on the result." You aren't calling the key function yourself; you're giving sorted() the tool it needs to do the job.

I generally recommend this approach whenever you find yourself writing three different functions that do almost the exact same thing, with only one small calculation being different. Instead of duplicating the logic, pull that one difference out into its own function and pass it in as an argument.




📋 Practical Task

Building a Flexible Log Message Formatter

Imagine you are building a logging system. Depending on the environment (Development, Production, or Debug), the log messages need to be formatted differently. Instead of using a bunch of if/else statements inside your logger, you're going to use first-class functions to handle the formatting.

Your Task:

  • Create three formatting functions:
    • dev_format(text): Returns the text wrapped in brackets, e.g., "[DEV] message".
    • prod_format(text): Returns the text in all uppercase, e.g., "MESSAGE".
    • debug_format(text): Returns the text with a timestamp prefix (you can just hardcode a string like "12:00 - message").
  • Create a function called log_message(message, formatter). This function should take the string message and the formatter function as arguments, and return the result of applying the formatter to the message.
  • Demonstrate that your log_message function works by calling it three times, passing a different formatter each time.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.