Skip to content

How to Understand Python Functions That Return Functions

I was reading some Python code the other day and came across this strange syntax:

weird_syntax.py
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:

test_double_parens.py
def foo():
print("foo called")
return bar
def bar():
print("bar called")
foo()()

When I ran this, I got:

output.txt
foo called
bar called

So foo()() was calling both foo and bar. But why? And how?

I then tried storing the result of foo() in a variable:

test_variable.py
def foo():
print("foo called")
return bar
def bar():
print("bar called")
result = foo()
print(f"result is: {result}")
result()

The output was:

output.txt
foo called
result is: <function bar at 0x7f8b1c2e3d90>
bar called

This 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:

  1. Assigned to variables
  2. Passed as arguments to other functions
  3. Returned from other functions
  4. Stored in data structures

Here’s the basic pattern for a function that returns another function:

basic_pattern.py
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:

three_ways.py
# Method 1: Store and call later
my_func = outer_function()
result = my_func() # "Hello from inner!"
# Method 2: Call immediately with double parentheses
result = outer_function()() # "Hello from inner!"
# Method 3: Pass to another function
def 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:

closure_example.py
def multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = multiplier(2)
triple = multiplier(3)
print(double(5)) # Returns 10
print(triple(5)) # Returns 15

The 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:

factory_example.py
def power_factory(exponent):
def power(base):
return base ** exponent
return power
square = power_factory(2)
cube = power_factory(3)
print(square(4)) # 16
print(cube(4)) # 64

2. Decorators

This pattern is the foundation of Python decorators:

decorator_pattern.py
def my_decorator(func):
def wrapper():
print("Before the function call")
func()
print("After the function call")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
# This is equivalent to: say_hello = my_decorator(say_hello)
say_hello()

Output:

decorator_output.txt
Before the function call
Hello!
After the function call

3. Function Collections

As one Reddit commenter pointed out, you can create lists or dictionaries of functions to run later:

function_collection.py
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

mistake_calling.py
# WRONG - returns None, not a function
def outer():
def inner():
return "result"
return inner() # Called with () - this returns "result", not a function
# CORRECT - returns the function object
def outer():
def inner():
return "result"
return inner # No parentheses - this returns the function itself

Mistake 2: Forgetting the Outer Function Needs to Be Called First

mistake_forgetting.py
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 function
outer()() # "result"

Mistake 3: Not Understanding Variable Scope in Closures

mistake_scope.py
# This often confuses people
def 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:

scope_fix.py
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:

keep_it_simple.py
# If you just need configuration, a class might be clearer
class 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 cases
double = Multiplier(2)
print(double.multiply(5)) # 10

Summary

Functions returning functions are a fundamental Python pattern. The key insights are:

  1. Functions are objects - They can be returned like any other value
  2. The double parentheses foo()() means call foo, then call what it returns
  3. Closures allow returned functions to remember their enclosing scope
  4. 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