Python Decorators and Closures


What is a Closure in Python?

A closure is a function object that remembers values in the enclosing scope even if the outer function has finished executing. This is a fundamental concept behind decorators.

Example: Python Closure

def outer(msg):
    def inner():
        print(f"Message: {msg}")
    return inner

my_func = outer("Hello from Closure!")
my_func()

Output:

Message: Hello from Closure!

Why is this a closure?

  • inner() is nested inside outer()
  • inner() uses a variable (msg) from the outer function
  • outer() returns the inner() function


Python Decorators Explained

A decorator is a higher-order function that takes a function as input and returns a new function with added functionality—without modifying the original function's code.

How Do Decorators Work?

Decorators typically:

  • Accept a function as an argument
  • Define an inner function to wrap additional logic
  • Return that inner function

Basic Python Decorator Example

def my_decorator(func):
    def wrapper():
        print("Before the function is called.")
        func()
        print("After the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

Output:

Before the function is called.
Hello!
After the function is called.

Example with Arguments

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Function is being called with arguments")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Output:

Function is being called with arguments
Hello, Alice!


Real-World Use Case: Authorization Decorator

def require_admin(func):
    def wrapper(user):
        if user == "admin":
            return func(user)
        else:
            print("Access Denied.")
    return wrapper

@require_admin
def access_dashboard(user):
    print(f"Dashboard accessed by {user}")

access_dashboard("admin")   # Access granted
access_dashboard("guest")   # Access denied

Output:

Dashboard accessed by admin
Access Denied.

Chaining Multiple Decorators

def uppercase(func):
    def wrapper():
        return func().upper()
    return wrapper

def greet_decorator(func):
    def wrapper():
        return f"Greeting: {func()}"
    return wrapper

@uppercase
@greet_decorator
def say_hi():
    return "hello"

print(say_hi())

Output:

GREETING: HELLO

Decorators are applied bottom-up.

Closure vs Decorator

Feature Closure Decorator
Purpose Preserve state of enclosing scope Extend behavior of function
Returns Inner function Inner function
Usage Reuse logic and encapsulate state Add features like logging, timing
Example Returning functions with preserved values @decorator syntax above functions

Built-in Decorators in Python

Decorator Purpose
@staticmethod Defines a static method in a class
@classmethod Defines a class method
@property Turns method into a read-only property
@functools.wraps Preserves original function metadata

Pro Tip: Use functools.wraps to Preserve Metadata

from functools import wraps

def log_function(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_function
def test():
    """This is a test function."""
    print("Running test.")

print(test.__name__)  # test
print(test.__doc__)   # This is a test function.