Fundamentals 5 min read

Understanding Python Closures and Decorators

This article explains Python closures and decorators, illustrating how closures capture free variables and how decorators can wrap functions to log calls, handle parameters, and preserve metadata, with clear code examples and step‑by‑step explanations.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Understanding Python Closures and Decorators

Python decorators resemble Java annotations but serve different purposes, enabling aspect‑oriented programming.

To understand decorators, one must first grasp closures, which are functions that capture free variables from their defining environment.

Example closure code:

# print_msg is the outer function

def print_msg():
    msg = "I'm closure"
    # printer is the nested function
    def printer():
        print(msg)
    return printer

# Obtain the closure
closure = print_msg()
# Output "I'm closure"
closure()

The variable msg is local to print_msg, but the nested function retains a reference, forming a closure.

A typical decorator example using functools.wraps prints the function name and arguments.

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('call %s():' % func.__name__)
        print('args = {}'.format(*args))
        return func(*args, **kwargs)
    return wrapper

Applying the decorator:

@log

def test(p):
    print(test.__name__ + " param: " + p)

test("I'm a param")

Output shows the call and arguments.

The @ syntax simply passes the function to the decorator; the same effect can be achieved by calling the decorator explicitly.

When using @functools.wraps(func), the original function’s metadata (name, docstring, etc.) is preserved; removing it changes test.__name__ to wrapper.

Decorators can also accept parameters, requiring three nested functions.

import functools

def log_with_param(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('call %s():' % func.__name__)
            print('args = {}'.format(*args))
            print('log_param = {}'.format(text))
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log_with_param("param")
def test_with_param(p):
    print(test_with_param.__name__)

Calling the decorator explicitly yields the same result.

These examples demystify decorators, highlighting Python’s first‑class functions and functional programming features.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Pythonfunctional programmingdecoratorsclosures
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.