How to Understand Python Functions That Return Functions
I was reading some Python code the other day and came across this strange syntax:
result = foo()()Double parentheses? What does foo()() even mean? I was confused. I understood that foo() calls a function, but why would there be another set of parentheses after that?
The Environment
I was working on a Python project where I needed to understand some decorator code. The codebase used this pattern extensively, and I couldn’t figure out what was happening.
System Environment:
- Python 3.11+
- Ubuntu 22.04
What Happened
I tried to understand this by experimenting in the Python REPL. First, I created a simple test:
def foo(): print("foo called") return bar
def bar(): print("bar called")
foo()()When I ran this, I got:
foo calledbar calledSo foo()() was calling both foo and bar. But why? And how?
I then tried storing the result of foo() in a variable:
def foo(): print("foo called") return bar
def bar(): print("bar called")
result = foo()print(f"result is: {result}")result()The output was:
foo calledresult is: <function bar at 0x7f8b1c2e3d90>bar calledThis was my aha moment. foo() returns a function object, not the result of calling that function. The returned function can then be called with another set of parentheses.
The Solution
The solution is to understand that functions in Python are first-class objects. This means they can be:
- Assigned to variables
- Passed as arguments to other functions
- Returned from other functions
- Stored in data structures
Here’s the basic pattern for a function that returns another function:
def outer_function(): def inner_function(): return "Hello from inner!" return inner_function # Return the function object, NOT inner_function()There are three ways to use this returned function:
# Method 1: Store and call latermy_func = outer_function()result = my_func() # "Hello from inner!"
# Method 2: Call immediately with double parenthesesresult = outer_function()() # "Hello from inner!"
# Method 3: Pass to another functiondef executor(func): return func()
result = executor(outer_function()) # "Hello from inner!"The Real Power: Closures
But wait, there’s more. The returned function can “remember” variables from its enclosing scope. This is called a closure:
def multiplier(factor): def multiply(number): return number * factor return multiply
double = multiplier(2)triple = multiplier(3)
print(double(5)) # Returns 10print(triple(5)) # Returns 15The multiply function “remembers” the factor variable from when it was created. Each call to multiplier creates a new function with its own factor value.
The Reason
I think the key reason this pattern is so useful is that it enables several powerful programming techniques:
1. Factory Functions
You can create specialized functions on-the-fly:
def power_factory(exponent): def power(base): return base ** exponent return power
square = power_factory(2)cube = power_factory(3)
print(square(4)) # 16print(cube(4)) # 642. Decorators
This pattern is the foundation of Python decorators:
def my_decorator(func): def wrapper(): print("Before the function call") func() print("After the function call") return wrapper
@my_decoratordef say_hello(): print("Hello!")
# This is equivalent to: say_hello = my_decorator(say_hello)say_hello()Output:
Before the function callHello!After the function call3. Function Collections
As one Reddit commenter pointed out, you can create lists or dictionaries of functions to run later:
def make_sound(animal): sounds = { 'dog': lambda: 'Woof!', 'cat': lambda: 'Meow!', 'cow': lambda: 'Moo!' } return sounds.get(animal, lambda: 'Unknown animal')
dog_sound = make_sound('dog')print(dog_sound()) # Woof!Common Mistakes
I made several mistakes while learning this pattern. Here are the ones to avoid:
Mistake 1: Calling the Inner Function Instead of Returning It
# WRONG - returns None, not a functiondef outer(): def inner(): return "result" return inner() # Called with () - this returns "result", not a function
# CORRECT - returns the function objectdef outer(): def inner(): return "result" return inner # No parentheses - this returns the function itselfMistake 2: Forgetting the Outer Function Needs to Be Called First
def outer(): def inner(): return "result" return inner
# WRONG - trying to call inner directly# outer()() is correct, but outer.inner() doesn't exist
# CORRECT - call outer first, then call the returned functionouter()() # "result"Mistake 3: Not Understanding Variable Scope in Closures
# This often confuses peopledef create_counters(): counters = []
for i in range(3): def counter(): return i counters.append(counter)
return counters
c1, c2, c3 = create_counters()print(c1(), c2(), c3()) # 2 2 2, not 0 1 2!Why? Because all the counter functions share the same i variable, which is 2 by the time the loop ends. The fix is to capture the value:
def create_counters(): counters = []
for i in range(3): def counter(value=i): # Capture the current value return value counters.append(counter)
return counters
c1, c2, c3 = create_counters()print(c1(), c2(), c3()) # 0 1 2 - correct!Mistake 4: Overusing When Simpler Patterns Would Work
Not everything needs to be a function-returning function. Sometimes a simple class or a straightforward function is clearer:
# If you just need configuration, a class might be clearerclass Multiplier: def __init__(self, factor): self.factor = factor
def multiply(self, number): return number * self.factor
# This is more explicit than a closure for complex casesdouble = Multiplier(2)print(double.multiply(5)) # 10Summary
Functions returning functions are a fundamental Python pattern. The key insights are:
- Functions are objects - They can be returned like any other value
- The double parentheses
foo()()means callfoo, then call what it returns - Closures allow returned functions to remember their enclosing scope
- This pattern enables factory functions, decorators, and powerful callback systems
I now understand that foo()() isn’t weird syntax - it’s just two function calls in one expression. The first () calls foo, which returns a function. The second () calls that returned function.
This pattern is essential for writing clean, reusable, and configurable code in Python. Once I understood it, I started seeing it everywhere - in decorators, factory patterns, and callback systems.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments