Key Takeaways
A closure is a function that, even after the outer function has completed its execution, retains values from its enclosing scope.
A nested function becomes a Python closure only when it captures one or more free variables from its enclosing function.
Closures rely on Python's lexical scoping rules to preserve access to captured variables.
Closures can store state, making them useful for building function factories and lightweight stateful functions.
Comprehending closures facilitates the explanation of sophisticated Python features like decorators and typical issues like late binding.
Introduction
Let's say a function yields another function. The local variables of the outer function should vanish after it is finished. However, the returning function continues to access those variables as if nothing had changed when you call it later.
How is that possible?
The answer lies in closures, one of Python's most powerful features for working with functions. With the use of a closure, a function can continue to access variables from its surrounding scope even after that scope has vanished. This behavior enables functions to preserve state, create specialised functions, and form the foundation of features like decorators.
Understanding what is closure in Python requires more than memorising a definition. It involves understanding how nested functions, scopes, and object references work together. Once these concepts become clear, closures become much easier to reason about.
By the end of this blog, you'll understand what a closure is, how Python creates closures, why they matter, and how they allow functions to retain state beyond the lifetime of their enclosing function.
What is Closure?
A closure is a function which, even after the enclosing function has completed its execution, retains and can access variables from its enclosing scope.
In Python, functions are first-class objects; they can be created inside other functions, passed as arguments, returned from functions, and assigned to variables. When an inner function retains access to variables defined in its enclosing function, it forms a closure.
In simple terms:
A closure is an inner function together with the variables it captures from its enclosing scope.
The captured variables are preserved because the returned function maintains a reference to them, allowing the function to continue using those values whenever it is called.
Understanding what is closure is important because it explains how Python enables functions to carry information beyond the lifetime of the function that created them.
Why Closures Exist
Consider a situation where you want a function to remember some information between calls without relying on global variables.
For example, suppose you want to create one function that always multiplies numbers by 2 and another that always multiplies numbers by 5. Rather than writing separate functions for every multiplier, you can write one function that creates customized multiplier functions.
For this to work, the returned function must remember the multiplier that was originally provided.
This is exactly why closures exist.
Instead of copying values into the returned function, Python allows the inner function to retain access to variables from its enclosing scope. These captured variables remain available even after the outer function completes execution.
A simple method for maintaining state inside functions while keeping it private is to use closures. They are therefore helpful for encapsulating data, developing customized methods, and avoiding needless global variables.
Understanding Nested Functions
A function written within another function is known as a nested function.
Example:
def outer():
def inner():
print("Hello")
inner()
outer()
In this case, inner() is limited to outer(). Direct calls to it from outside the enclosing function are not possible.
Nested functions are useful because they allow related functionality to be grouped together and enable the inner function to access names defined in the enclosing function.
For example:
def outer():
message = "Hello"
def inner():
print(message)
inner()
outer()
The inner function can access message because it is defined in the enclosing scope.
However, a nested function is not automatically a closure.
A nested function becomes a closure only when:
- it references one or more variables from its enclosing function, and
- it continues to access those variables after the outer function has returned.
This distinction is important because every closure is a nested function, but not every nested function forms a closure.
What Makes a Python Closure?
A Python closure is created only when three conditions are met:
- An outer function defines one or more local variables.
- An inner function references one or more of those variables.
- The outer function returns the inner function.
Consider this closure example:
def greeting(text):
def greet(name):
return f"{text}, {name}!"
return greet
Here:
greeting() is the outer function.
greet() is the inner function.
text is a free variable because it belongs to the enclosing function but is used inside the inner function.
Returning greet creates a closure.
Even after greeting() finishes execution, the returned function still remembers the value of text.
This ability to preserve access to variables from an enclosing scope is what distinguishes a Python closure from an ordinary nested function. It combines functions, lexical scope, and object references to create functions that carry their own state wherever they are used.
How Closures Capture Variables
A closure is more than a nested function. Its defining feature is that it captures variables from its enclosing scope and continues to use them even after the enclosing function has finished executing.
A common misconception is that a closure copies the values of those variables. It doesn't.
Instead, a closure retains references to the variables defined in the enclosing scope. As long as the closure exists, Python keeps those variables alive, allowing the inner function to access them whenever it is called.
This behavior is what enables closures to preserve state and makes them useful for creating specialized functions.
Free Variables
A free variable is a variable that is used inside a function but defined in its enclosing function, rather than within the function itself.
Consider this example:
def outer():
message = "Welcome"
def inner():
print(message)
return inner
In this example:
message is created inside outer().
inner() uses message but does not define it.
Therefore, message is a free variable for inner().
Free variables are the foundation of closures. Without them, an inner function has no state to capture, and no closure is formed.
Lexical Scoping
Python determines where a function looks for names based on where the function is defined, not where it is called. This behavior is known as lexical scoping (also called static scoping).
When Python encounters a name inside a function, it searches according to the LEGB rule:
- Local scope
- Enclosing scope
- Global scope
- Built-in scope
For a closure, the enclosing scope is especially important.
def outer():
language = "Python"
def inner():
print(language)
return inner
Here, language is not a local variable of inner(). During name resolution, Python doesn't find it in the local scope, so it searches the enclosing scope and finds language inside outer().
Because of lexical scoping, the inner function always remembers the environment where it was defined, regardless of where it is called later.
Remembering Data After the Outer Function Returns
One of the most surprising aspects of closures is that they continue to access variables even after the outer function has completed execution.
Normally, local variables exist only while a function is running. Once the function returns, its local namespace is destroyed.
Closures are different.
When an outer function returns an inner function that references free variables, Python preserves those variables instead of discarding them. The closure keeps references to the captured variables, allowing the inner function to continue using them.
For example:
def outer():
message = "Hello"
def inner():
return message
return inner
greet = outer()
print(greet())
Output
Hello
Although outer() has already finished executing, greet() still accesses message.
The variable survives because the closure still references it, not because the outer function is still running.
This ability to preserve data beyond the lifetime of a function is what makes closures unique.
Closure Example in Python
Let's see how a closure example works step by step.
def multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = multiplier(2)
print(double(5))
Output
10
Here's what happens during execution:
multiplier(2) is called.
The local variable factor is assigned the value 2.
Python creates the inner function multiply().
multiply() references factor, making it a free variable.
multiplier() returns the inner function.
Even though multiplier() has finished executing, the returned function retains access to factor.
Calling double(5) multiplies 5 by the remembered value 2.
Notice that the returned function doesn't receive factor as an argument. Instead, it retrieves the value from its captured enclosing scope.
This is the defining characteristic of a closure, it combines a function with the variables it remembers.
Inspecting a Closure (__closure__ and cell_contents)
Python allows you to inspect the variables captured by a closure through the __closure__ attribute.
Using the previous example:
def multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = multiplier(2)
print(double.__closure__)
If we want to check what is the closure for a function, we use the '__closure__' attribute that gives us a set of memory cell objects. Each cell object is holding one of the captured variable values.
To view the actual value stored inside a cell, use the cell_contents attribute.
print(double.__closure__[0].cell_contents)
Output
2
Here:
The variables that the closure has captured are contained in __closure__.
cell_contents retrieves the value stored in each captured variable.
Inspecting these attributes helps you understand that a closure doesn't simply remember values conceptually, it actually stores references to the variables it captures. This mechanism enables a closure to preserve state long after its enclosing function has returned.
Using nonlocal with Closures
A closure can read variables from its enclosing scope without any extra syntax. However, reading a variable and modifying it are two different operations.
If an inner function only accesses an enclosing variable, Python automatically finds it using lexical scoping. But if the inner function tries to assign a new value to that variable, Python treats it as a new local variable unless instructed otherwise.
By indicating to Python that a variable is part of the closest enclosing function, the nonlocal keyword enables the inner function to change an existing variable rather than create a new one.
Example:
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
next_count = counter()
print(next_count())
print(next_count())
Output
1
2
Here, count belongs to the enclosing function counter(). Using nonlocal allows increment() to update the same variable each time it is called.
Without nonlocal, the assignment would create a local variable named count, and the closure would no longer modify the captured variable.
Why Rebinding Requires nonlocal
One of Python's scoping rules is that assignment creates a local variable unless stated otherwise.
Consider this example:
def counter():
count = 0
def increment():
count += 1
return count
return increment
This code raises an error because the assignment count += 1 tells Python that count is a local variable inside increment(). However, Python attempts to read the variable before it has been assigned, resulting in an UnboundLocalError.
The correct approach is:
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
The nonlocal declaration changes the binding behavior. Instead of creating a new local variable, Python updates the existing variable from the enclosing function.
Use nonlocal only when an inner function needs to rebind an enclosing variable. If the variable is only being read, no declaration is required.
How Closures Store State
One of the biggest advantages of closures is their ability to store state between function calls.
Normally, local variables disappear after a function returns. With a closure, captured variables remain available because the returned function continues to reference them.
Consider this example:
def create_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = create_counter()
print(counter())
print(counter())
print(counter())
Output
1
2
3
Although create_counter() executes only once, the closure remembers the value of count across multiple calls.
The state belongs to the closure itself, not to the outer function. Each time create_counter() is called, Python creates a new closure with its own independent state.
counter1 = create_counter()
counter2 = create_counter()
print(counter1())
print(counter1())
print(counter2())
Output
1
2
1
Every closure keeps a copy of the captured state for itself.
Function Factories
A function that generates and returns other functions is called a function factory.
Because the returning function retains the values given when it was constructed, closures enable function factories.
Example:
def power(exponent):
def calculate(base):
return base ** exponent
return calculate
square = power(2)
cube = power(3)
print(square(4))
print(cube(4))
Output
16
64
Here's what happens:
power(2) creates a function that always squares numbers.
power(3) creates another function that always cubes numbers.
Each returned function remembers its own value of exponent.
Without closures, you would need to repeatedly pass the exponent every time you call the function. Closures eliminate this repetition by preserving the required data automatically.
Function factories are a practical application of closures because they allow you to generate customized functions without duplicating code.
Closures and Decorators
Closures are the foundation of Python decorators.
A decorator being a function that takes a different function, generates an inner function, and then gives that inner function back. The returned function remembers objects from its enclosing scope, making it a closure.
A simplified example looks like this:
def decorator(func):
def wrapper():
print("Before function")
func()
return wrapper
In this example:
wrapper() is the inner function.
func is a free variable captured from the enclosing function.
Returning wrapper() creates a closure.
Although decorators introduce additional concepts, they rely on the same mechanism that powers closures: an inner function remembering variables from its enclosing scope.
For this reason, understanding closures first makes decorators much easier to learn. Almost every Python decorator uses closures to preserve information between the time the decorator is applied and the time the decorated function is eventually called.
The Late Binding Pitfall
Closures capture variables, not the values those variables hold at the time the closure is created. This behavior is known as late binding, and it is one of the most common reasons beginners find closures confusing.
Consider the following example:
functions = []
for i in range(3):
def show():
print(i)
functions.append(show)
for function in functions:
function()
Output
2
2
2
At first glance, you might expect the output to be:
0
1
2
However, all three functions print 2.
This happens because each closure references the same variable i, not the value of i during each iteration. By the time the functions are called, the loop has completed, and i holds its final value, 2.
One common solution is to bind the current value as a default argument.
functions = []
for i in range(3):
def show(value=i):
print(value)
functions.append(show)
for function in functions:
function()
Output
0
1
2
Understanding late binding is essential because it explains why closures sometimes behave differently from what new Python programmers expect.
Closures vs Regular Functions
Every closure is a function, but not every function is a closure.
A regular function performs its task using local variables, parameters, or global names. Once it finishes execution, its local variables are discarded.
A closure, on the other hand, carries references to variables from its enclosing scope, allowing it to retain state even after the outer function has returned.
| Regular Function | Closure |
|---|---|
| Executes independently | Retains variables from an enclosing scope |
| Local variables disappear after execution | Captured variables remain available |
| Does not preserve state between calls | Can preserve state across calls |
| Suitable for general-purpose tasks | Suitable for stateful or customized behavior |
If a function does not capture free variables from an enclosing scope, it is simply a regular function rather than a closure.
Closures vs Classes
Closures and classes can both preserve state, but they solve the problem differently.
A closure stores state through captured variables, whereas a class stores state in instance attributes.
| Closure | Class |
|---|---|
| Stores state using captured variables | Stores state using object attributes |
| Lightweight and concise | Better for complex state and behavior |
| Ideal for a single operation | Suitable for multiple related operations |
| Doesn't require creating a class | Organizes large programs more effectively |
Choose a closure when you need a small amount of state for a single function.
Choose a class when multiple methods need to share and modify the same state or when the logic becomes more complex.
When Closures Are Useful
Closures are particularly useful when a function needs to remember information without exposing that information as a global variable.
Some common use cases include:
- Creating specialized functions through function factories.
- Maintaining state across several function calls.
- Encapsulating implementation details inside a function.
- Building decorators that wrap and extend existing functions.
- Reducing reliance on global variables.
Closures provide a simple way to combine behavior and state while keeping the implementation private and reusable.
Best Practices
Following a few simple practices can make closures easier to understand and maintain.
Capture Only What You Need
Capture only the variables required by the inner function. Avoid unnecessarily retaining objects that are no longer needed.
Use nonlocal Only When Required
Use nonlocal only if the inner function needs to modify an enclosing variable. Reading an enclosing variable does not require it.
Keep Closures Focused
For your closures to be most effective, limit them to performing a single task clearly and thoroughly. It is hard with closures that do many different things at the same time, to figure out what's going on or find out where a problem is coming from.
Prefer Classes for Complex State
When a closure needs to deal with several variables or different actions, you can expect that a class will make things easier and keep the overall structure of your code stable.
Watch for Late Binding
Whenever you create a closure within a loop, take into account that the closure will hold a reference to the variable rather than a copy of the current value of the variable. That way, if it should be closure different closures which each remember their own values, that variable should be bound first before the closure is created.
Common Mistakes
Mistaking Closures for Nested Functions
Of course every closure is a nested function, but a nested function only becomes a closure when it has captured some of the variables of the surrounding scope.
Assuming Closures Copy Values
Copies of variables are not kept in closures. In the enclosing scope, they keep references to the original variables.
Forgetting nonlocal
When rebinding a captured variable, omitting the nonlocal keyword causes Python to create a new local variable instead of modifying the existing one.
Ignoring the Late Binding Behavior
Closures created inside loops often produce unexpected results because all of them reference the same loop variable.
Using Closures for Large Applications
Closures are excellent for lightweight state management. For applications requiring multiple operations or extensive shared state, classes are usually easier to understand and maintain.
Summary
A closure function is just an inner function that takes the outer function scope and keeps it. You will remember this as what Closure means here in Python, with you understanding how, through lexical scope, they capture free variables, the reason nonlocal needs to be used for rebinding, and closures retaining state. You have also gone through function factories, how closures compare to decorators, and the late binding mistake. Besides closures vs classes, there are also other considerations when deciding how to best structure your program! Learning these things will enable you to better see how Python joins functions, scope, and instance references into a mechanism for developing flexible and reuseful code that is powerful enough.
Frequently Asked Questions
What is closure in Python?▾
A closure is an inner function that remembers and accesses variables from its enclosing scope even after the outer function has returned.
What is the difference between a nested function and a closure?▾
A function placed within another function is called a nested function. Only when it catches one or more free variables from its surrounding scope does it become a closure.
What is a free variable?▾
A free variable is a variable that is used inside a function but defined in its enclosing function rather than within the function itself.
Why do closures use the nonlocal keyword?▾
Instead of generating a new local variable, the nonlocal keyword enables an inner function to change a variable from its outer function.
How do closures store state?▾
Closures allow captured variables to be accessible during several function calls by keeping references to those variables.
What is the late binding problem?▾
Late binding occurs because closures capture variables rather than their values. If the variable changes later, the closure uses its most recent value.
When should I use a closure instead of a class?▾
Use a closure for small, focused tasks that require a little state. Choose a class when managing complex state or multiple related behaviors.
How are closures related to decorators?▾
Decorators use closures to remember the function being decorated and any additional data needed when the decorated function is called.


