Decorators in Python
are a powerful and convenient way to modify or enhance the behavior of functions or methods. They are essentially functions that wrap other functions to extend their behavior without explicitly modifying them. Here's a breakdown of how decorators work with examples.

A decorator is a function that takes another function as an argument and returns a new function that usually extends the behavior of the original function. Decorators are often used for logging, access control, instrumentation, caching, and more.
Basic Decorator Example
Let's start with a simple example of a decorator that prints a message before and after calling the original function.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Explanation:
my_decorator
is a decorator function that takesfunc
as an argument.- Inside
my_decorator
, we define a nested functionwrapper
that prints a message, calls the original functionfunc
, and then prints another message. - The
my_decorator
function returns thewrapper
function. - The
@my_decorator
syntax is a shorthand forsay_hello = my_decorator(say_hello)
. - When
say_hello()
is called, thewrapper
function is executed, which includes the additional print statements.
Decorators with Arguments
Decorators can also accept arguments. Here’s an example of a decorator that takes an argument:
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Explanation:
repeat
is a decorator factory that takes an argumentnum_times
.repeat
returns the actual decoratordecorator_repeat
.decorator_repeat
takes the functionfunc
to be decorated.- Inside
decorator_repeat
, we definewrapper
that callsfunc
num_times
times. - The
@repeat(num_times=3)
syntax applies the decorator togreet
, so callinggreet("Alice")
will print the greeting three times.
Using functools.wraps
When you use decorators, the metadata of the original function (such as its name, docstring) can be lost. To preserve this metadata, you can use functools.wraps
.
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def say_hello():
"""This is the say_hello function."""
print("Hello!")
print(say_hello.__name__) # Output: say_hello
print(say_hello.__doc__) # Output: This is the say_hello function.
Explanation:
functools.wraps(func)
is a decorator that updates thewrapper
function to look likefunc
by copying attributes such as the name and docstring.- This ensures that the decorated function retains the metadata of the original function.
Decorators are a versatile feature in Python that can significantly enhance the readability and maintainability of your code by separating concerns and reducing boilerplate.