MyInternships.in

Python Functions

Python Decorators

A decorator is a function that wraps another function to add extra behaviour — like logging, timing, or access checks — without changing the original function's code.


How Decorators Work

A decorator is just a function that takes another function as input, defines a wrapper function around it, and returns that wrapper. Because it relies on an inner function capturing the original one, decorators are a direct, practical use of closures.

A basic decorator, the long way
Python
def shout(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

def greet():
    return "hello"

greet = shout(greet)   # manually wrapping
print(greet())         # HELLO

The @ Syntax

Writing @decorator_name directly above a function definition is shorthand for the manual wrapping shown above — Python applies it automatically when the function is defined.

Using @ syntax
Python
def shout(func):
    def wrapper():
        return func().upper()
    return wrapper

@shout
def greet():
    return "hello"

print(greet())   # HELLO

Handling Arguments with *args and **kwargs

Real functions usually take arguments, so a general-purpose decorator's wrapper should accept and forward any arguments using *args and **kwargs.

A timing decorator
Python
import time
import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f}s")
        return result
    return wrapper

@timer
def slow_add(a, b):
    time.sleep(0.2)
    return a + b

print(slow_add(3, 4))

Why functools.wraps Matters

Without functools.wraps, the wrapped function loses its original name and docstring — func.__name__ becomes "wrapper" for every decorated function, which makes debugging and introspection confusing. @functools.wraps(func) fixes this by copying that metadata over.

⚠️

Always add @functools.wraps(func) inside your decorator's wrapper — skipping it is a very common beginner mistake that breaks help() and debugging tools.

A Logging Decorator Example

Logging every call
Python
import functools

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

@log_calls
def add(a, b):
    return a + b

add(2, 3)   # Calling add(2, 3)

Related Python Topics

Keep learning with these closely related lessons.

Ready to use your Python skills?

Find Python, data science and software internships and fresher jobs across India.

Browse Python Internships