Python Closures – Definition, Syntax & Examples

Introduction

Python is known for its flexibility and powerful programming features. One of these advanced features is Closures.

Closures are widely used in Python for data hiding, decorators, callbacks, event handling, and functional programming. Although they may seem complicated at first, understanding closures can significantly improve your ability to write clean, reusable, and efficient code.

For example, you might want a function that always multiplies numbers by a specific value. Using closures, you can create multiple multiplier functions without repeatedly writing the same code.

What are Python Closures?

A closure is a nested function that remembers and can access variables from its outer function even after the outer function has completed execution.

In simple words:

A closure allows an inner function to retain access to the variables of its enclosing scope

Example:


def outer_function():
   message = "Hello Python"
   def inner_function():
       print(message)
   return inner_function

Usage:


greet = outer_function()
greet()

Output:

Hello Python

Even though outer_function() has already finished execution, the inner function still remembers the value of message.

Why Use Closures?

Closures offer several advantages.

1. Data Encapsulation

Hide variables from outside access.

2. State Preservation

Remember values between function calls.

3. Function Factories

Create customized functions dynamically.

4. Lightweight Alternative to Classes

Maintain state without creating objects.

5. Useful for Decorators

Closures form the foundation of Python decorators.

Understanding Nested Functions

Closures depend on nested functions.

A nested function is simply a function defined inside another function.

Example:


def outer():
   def inner():
       print("Inside Inner Function")
   inner()

Usage:


outer()

Output:

Inside Inner Function

The inner function exists inside the outer function.

How Closures Work?

A closure occurs when:

  1. A nested function exists.
  2. The inner function references variables from the outer function.
  3. The outer function returns the inner function.

Example:


def outer():
   value = 10
   def inner():
       print(value)
   return inner

Usage:


function = outer()
function()

Output:

10

The variable value remains available even after outer() finishes execution.

Syntax

Basic syntax:


def outer_function():
   variable = value
   def inner_function():
       # Use variable
   return inner_function

The returned inner function becomes a closure.

Simple Closure Example


def greeting():
   message = "Welcome"
   def display():
       print(message)
   return display

Usage:


show = greeting()
show()

Output:

Welcome

The closure remembers the value of message.

Closure with Parameters

Closures become more useful when parameters are involved.

Example:


def multiplier(number):
   def multiply(value):
       return value * number
   return multiply

Usage:


double = multiplier(2)
print(double(5))

Output:

10

The closure remembers that number equals 2.

Creating Multiple Closures

Example:


def multiplier(number):
   def multiply(value):
       return value * number
   return multiply

Usage:


double = multiplier(2)
triple = multiplier(3)
print(double(5))
print(triple(5))

Output:

10
15

Each closure maintains its own state.

Closure Preserving State

Closures can remember data between calls.

Example:


def counter():
   count = 0
   def increment():
       nonlocal count
       count += 1
       return count
   return increment

Usage:


c = counter()
print(c())
print(c())
print(c())

Output:

1
2
3

The closure remembers the updated value of count.

Understanding the nonlocal Keyword

The nonlocal keyword allows modification of variables in the enclosing scope.

Without nonlocal:


def outer():
   count = 0
   def inner():
       count += 1

Output:

UnboundLocalError

Correct:


nonlocal count

This tells Python to use the variable from the outer function.

Inspecting Closure Variables

Python provides the __closure__ attribute.

Example:


def outer():
   message = "Python"
   def inner():
       print(message)
   return inner

Usage:


func = outer()
print(func.__closure__)

Output:

(<cell at …>,)

The closure stores references to outer variables.

Real-Life Examples:

1. Discount Calculator


def discount(rate):
   def calculate(price):
       return price - (
           price * rate / 100
       )
   return calculate

Usage:


discount10 = discount(10)
print(
   discount10(1000)
)

Output:

900

The closure remembers the discount rate.

2. User Authentication


def authenticate(username):
   def check(user):
       return user == username
   return check

Usage:


admin_check = authenticate("admin")
print(
   admin_check("admin")
)

Output:

True

The closure stores the authorized username.

3. Tax Calculator


def tax_calculator(rate):
   def calculate(amount):
       return amount + (
           amount * rate / 100
       )
   return calculate

Usage:


gst = tax_calculator(18)
print(gst(1000))

Output:

1180

The tax rate is preserved inside the closure.

Closures vs Global Variables

Using Global Variables:


rate = 10
def calculate(price):
   return price * rate

Problems:

  • Harder to manage
  • Can be modified anywhere

Using Closures:


def create_calculator(rate):
   def calculate(price):
       return price * rate
   return calculate

Benefits:

  • Safer
  • Encapsulated
  • More reusable

Closures vs Classes

Closure Example


def counter():
   count = 0
   def increment():
       nonlocal count
       count += 1
       return count
   return increment

Class Example


class Counter:
   def __init__(self):
       self.count = 0
   def increment(self):
       self.count += 1
       return self.count

Both maintain state.

Closures are simpler for small tasks, while classes are better for complex applications.

Closures and Decorators

Decorators are built using closures.

Example:


def decorator(func):
   def wrapper():
       print("Before")
       func()
       print("After")
   return wrapper

Here, wrapper() is a closure because it remembers func.

Advantages of Closures

Advantage Description
Data Hiding Variables remain private
State Preservation Remembers values
Reusability Creates customized functions
Cleaner Code Less boilerplate
Useful in Decorators Foundation of decorators

Disadvantages of Closures

Disadvantage Description
Harder to Understand Complex for beginners
Debugging Challenges Nested functions can be difficult to trace
Limited Scalability Classes may be better for large systems

Common Mistakes

1. Forgetting to Return the Inner Function

Incorrect:


def outer():
   def inner():
       pass

Correct:


return inner

2. Calling the Inner Function Immediately

Incorrect:


return inner()

This returns the result instead of the function.

Correct:


return inner

3. Modifying Outer Variables Without nonlocal

Incorrect:


count += 1

Correct:


nonlocal count
count += 1

4. Confusing Closures with Nested Functions

Not every nested function is a closure.

A closure must:

  • Reference outer variables.
  • Be returned from the outer function.

5. Overusing Closures

Closures are useful, but for large applications, classes may provide better organization.

Conclusion

Python Closures are a powerful feature that allows functions to remember and access variables from their enclosing scope even after the outer function has completed execution. They provide an elegant way to preserve state, encapsulate data, create function factories, and build decorators. Closures are particularly useful when you need lightweight state management without creating classes.

Related Python Tutorials