CodeToLive

Decorators in Python

Decorators in Python are used to modify the behavior of a function or method. They are often used for logging, access control, memoization, and more. Decorators provide a clean and reusable way to extend the functionality of functions.

Defining a Decorator

A decorator is a function that takes another function as input and returns a new 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()
      

Decorators with Arguments

You can create decorators that accept arguments by adding an extra layer of nesting.


def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator

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

greet("Alice")
      

Class-Based Decorators

Decorators can also be implemented using classes. This is useful when you need to maintain state.


class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__}")
        return self.func(*args, **kwargs)

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

say_hello()
say_hello()
      

Chaining Decorators

You can apply multiple decorators to a single function. They are executed in the order they are listed.


def decorator1(func):
    def wrapper():
        print("Decorator 1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2")
        func()
    return wrapper

@decorator1
@decorator2
def say_hello():
    print("Hello!")

say_hello()
      

Built-in Decorators

Python provides several built-in decorators, such as @staticmethod, @classmethod, and @property.


class MyClass:
    @staticmethod
    def static_method():
        print("This is a static method.")

    @classmethod
    def class_method(cls):
        print(f"This is a class method of {cls.__name__}.")

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

obj = MyClass()
obj.static_method()
obj.class_method()
obj.name = "Alice"
print(obj.name)
      

Practical Use Cases

Decorators are commonly used for:


import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time:.4f} seconds to run.")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)
    print("Function executed.")

slow_function()
      
Back to Tutorial