w3resource

Understanding Python Closures: Concepts and Practical Examples

Introduction to Python closures Functions

Closures in Python are a powerful feature that allows a function to retain access to its enclosing scope's variables even after the outer function has finished execution. Closures are used to create functions with some pre-configured behavior, making them particularly useful in decorators and callback functions. This tutorial will focus on examples to help you understand how closures work with maximum practical code.

Example 1: Basic Closure Example

This example demonstrates a basic closure, where an inner function retains access to variables from its enclosing function even after the outer function has completed.

Code:

def outer_function(msg):
    # Outer function that takes a message as an argument
    def inner_function():
        # Inner function that references the outer function's variable
        print(msg)
    return inner_function  # Return the inner function

# Create a closure
closure = outer_function("Hello, World!")

# Call the closure
closure()  # Output: Hello, World! 

Explanation:

  • Outer Function: 'outer_function()' defines a local variable 'msg' and an inner function 'inner_function()' that accesses msg.
  • Returning the Inner Function: The outer function returns 'inner_function()' without executing it.
  • Closure: When we call 'closure()', it still has access to 'msg', even though 'outer_function()' has finished executing.

Example 2: Closure with a Counter

This example shows how closures can maintain state between function calls, demonstrated by a simple counter that remembers its value.

Code:

def make_counter():
    # Outer function that initializes a counter
    count = 0

    def increment():
        # Inner function that increments the counter
        nonlocal count
        count += 1
        return count

    return increment  # Return the inner function

# Create a counter closure
counter = make_counter()

# Call the counter multiple times
print(counter())  # Output: 1
print(counter())  # Output: 2
print(counter())  # Output: 3

Explanation:

  • Outer Function: 'make_counter()' initializes a variable count and defines an inner function 'increment()' that modifies count.
  • 'nonlocal' Keyword: 'nonlocal' is used to modify the count variable in the enclosing scope.
  • Closure: The counter closure maintains its own count state across multiple calls.

Example 3: Customizing Functions with Closures

This example demonstrates how closures can create customized functions, such as a multiplier, that carry specific behavior based on the outer function's arguments.

Code:

def multiplier(factor):
    # Outer function that takes a factor
    def multiply(number):
        # Inner function that multiplies a number by the factor
        return number * factor
    return multiply  # Return the inner function

# Create closures with different factors
double = multiplier(2)
triple = multiplier(3)

# Use the closures
print(double(5))  # Output: 10
print(triple(5))  # Output: 15

Explanation:

  • Outer Function: 'multiplier()' takes a 'factor' and defines an inner function 'multiply()' that multiplies a given number by this factor.
  • Closures: The 'double' and 'triple' closures are customized functions that multiply numbers by 2 and 3, respectively.

Example 4: Closures as Decorators

This example illustrates how closures can be used as decorators, adding additional functionality (like logging) to existing functions without modifying their code.

Code:

def logger(func):
    # Outer function that takes a function as an argument
    def log_wrapper(*args, **kwargs):
        # Inner function that logs the function call and then executes it
        print(f"Calling {func.__name__} with arguments: {args}, {kwargs}")
        return func(*args, **kwargs)
    return log_wrapper  # Return the inner function

# Applying the closure as a decorator
@logger
def add(x, y):
    return x + y

# Call the decorated function
print(add(3, 4))  # Output: Calling add with arguments: (3, 4), {}
                  #         7

Explanation:

  • Outer Function: 'logger()' takes a function as an argument and defines an inner function 'log_wrapper()' that adds logging behavior.
  • Decorator: The 'log_wrapper()' closure logs the call details and then calls the original function.
  • Using the Closure: The 'add()' function is decorated with the 'logger' closure, so it logs its arguments whenever it is called.

Example 5: Closures for Data Encapsulation

This example demonstrates using closures for data encapsulation, creating an account with operations that securely manage the balance without exposing it directly.

Code:

def account(initial_balance):
    # Outer function that initializes balance
    balance = initial_balance

    def get_balance():
        # Inner function to get the balance
        return balance

    def deposit(amount):
        # Inner function to deposit money
        nonlocal balance
        balance += amount
        return balance

    def withdraw(amount):
        # Inner function to withdraw money
        nonlocal balance
        if amount <= balance:
            balance -= amount
            return balance
        else:
            return "Insufficient funds"

    # Return a dictionary of functions for encapsulated operations
    return {"get_balance": get_balance, "deposit": deposit, "withdraw": withdraw}

# Create an account closure
my_account = account(100)

# Perform operations
print(my_account["get_balance"]())  # Output: 100
print(my_account["deposit"](150))    # Output: 250
print(my_account["withdraw"](200))   # Output: 50

Explanation:

  • Data Encapsulation: The 'account()' function encapsulates the balance and provides controlled access via closures.
  • Operations: The closures ('get_balance', 'deposit', 'withdraw') manipulate and access the balance securely without exposing it directly.
  • Using the Closures: Operations on the account are performed using the returned closures, ensuring data encapsulation.


Become a Patron!

Follow us on Facebook and Twitter for latest update.

It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.

https://198.211.115.131/python/python-closures-with-examples.php