100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Python

Decorators in Python

Learn how Python decorators use the @syntax to wrap functions, adding behavior like logging or timing without changing the original function's code.

Modules, Files & IterablesAdvanced10 min readJul 7, 2026
Analogies

1. Introduction

A decorator is a function that takes another function as input and returns a new function that adds extra behavior around it, without modifying the original function's source code. Decorators are a powerful way to reuse cross-cutting logic, such as logging, timing, caching, or access control, across many functions.

🏏

Cricket analogy: A decorator is like adding a third umpire's review overlay to every delivery without changing how the bowler actually bowls—it wraps the action with extra scrutiny like timing or logging, reusable across every over.

Python provides special '@' syntax to apply a decorator to a function definition, making the wrapping explicit and easy to read at the point where the function is declared.

🏏

Cricket analogy: The "@" syntax for decorators is like a scorecard annotation placed right next to a batsman's name noting "review pending," making the extra behavior visible exactly where the player is declared in the lineup.

2. Syntax

python
def my_decorator(func):
    def wrapper(*args, **kwargs):
        # code before calling the original function
        result = func(*args, **kwargs)
        # code after calling the original function
        return result
    return wrapper

@my_decorator
def greet(name):
    return f'Hello, {name}!'

# The above is equivalent to:
# def greet(name):
#     return f'Hello, {name}!'
# greet = my_decorator(greet)

3. Explanation

Writing '@my_decorator' above a function definition is shorthand for 'greet = my_decorator(greet)': the original function is passed into the decorator, and whatever the decorator returns replaces the original name. The inner 'wrapper' function typically accepts *args and **kwargs so it can forward any arguments to the wrapped function, regardless of its signature.

🏏

Cricket analogy: "@my_decorator" reassigning greet is like team management deciding a bowler is now "the death-over specialist," replacing his general role with a specialized one that still accepts any batsman lineup thrown at him, like *args/**kwargs.

A common pitfall is that the wrapper function replaces the original function's identity, so introspection attributes like __name__ and __doc__ end up pointing to 'wrapper' instead of the original function. The functools.wraps decorator, applied to the wrapper, copies over these metadata attributes to fix this.

🏏

Cricket analogy: Without functools.wraps, a scorecard would list a substitute's name instead of the original player's, even though the substitute is filling in—wraps fixes this so the "credit" (name, stats) stays with the real player.

Without functools.wraps, a decorated function's __name__, __doc__, and other metadata get silently replaced with the wrapper's own values, which can break tools that rely on introspection (like debuggers or documentation generators). Always apply @functools.wraps(func) to the inner wrapper function.

4. Example

python
import functools

def log_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f'Calling {func.__name__} with {args}')
        result = func(*args, **kwargs)
        print(f'{func.__name__} returned {result}')
        return result
    return wrapper

@log_call
def add(a, b):
    """Add two numbers."""
    return a + b

total = add(3, 4)
print(total)
print(add.__name__)
print(add.__doc__)

5. Output

text
Calling add with (3, 4)
add returned 7
7
add
Add two numbers.

6. Key Takeaways

  • A decorator is a function that wraps another function to add behavior without changing its source code.
  • '@decorator' above a function definition is shorthand for 'func = decorator(func)'.
  • The inner wrapper typically uses *args and **kwargs to accept any arguments and forward them to the original function.
  • functools.wraps(func) preserves the original function's __name__, __doc__, and other metadata on the wrapper.
  • Decorators are commonly used for logging, timing, caching, authentication, and validation.

Practice what you learned

Was this page helpful?

Topics covered

#Python#PythonProgrammingStudyNotes#Programming#DecoratorsInPython#Decorators#Syntax#Explanation#Example#StudyNotes#SkillVeris