Skip to Content
Course content

Decorators are a powerful and flexible feature in Python, allowing you to modify or extend the behavior of functions or methods without changing their code. They are often used for logging, access control, memoization, and other tasks that involve modifying or enhancing existing functions.

1. What are Decorators?

A decorator is a function that takes another function (or method) as an argument and extends or alters its behavior. You can think of a decorator as a wrapper that "wraps" the original function and can modify its behavior before or after the function is called.

Decorators are commonly used for:

  • Code reusability: Apply a certain behavior across multiple functions without modifying each one.
  • Separation of concerns: Add functionality (like logging, validation, or authentication) to functions without modifying their core logic.
  • Aspect-oriented programming: Keep concerns like logging or caching separate from business logic.

2. How Decorators Work

In Python, decorators work by using higher-order functions. A higher-order function is a function that takes another function as an argument and returns a function.

Basic Syntax of a Decorator:

def decorator_function(original_function):
    def wrapper_function():
        print("Wrapper function executed before {}".format(original_function.__name__))
        return original_function()  # Call the original function
    return wrapper_function  # Return the wrapper function

@decorator_function  # This is equivalent to: my_function = decorator_function(my_function)
def my_function():
    print("Hello, World!")

my_function()

Output:

Wrapper function executed before my_function
Hello, World!

In the above example:

  • decorator_function is a decorator that takes my_function as an argument and returns the wrapper_function.
  • The @decorator_function syntax is a shorthand for passing my_function to the decorator.

3. Decorators with Arguments

You can also pass arguments to the function wrapped by the decorator. The decorator function can accept arguments if needed, making it more flexible.

Example: A Decorator with Arguments

def decorator_with_args(arg):
    def decorator_function(original_function):
        def wrapper_function(*args, **kwargs):
            print(f"Decorator argument value: {arg}")
            return original_function(*args, **kwargs)
        return wrapper_function
    return decorator_function

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

greet("Alice")

Output:

Decorator argument value: Hello
Hello, Alice!

In this example, the decorator decorator_with_args takes an argument arg and passes it to the wrapper_function, which can use it as needed.

4. Built-in Python Decorators

Python provides several built-in decorators that are commonly used in Python programming. Some of them include:

a) @staticmethod

A static method doesn’t require access to the instance or class and is usually used for utility functions.

class MyClass:
    @staticmethod
    def greet(name):
        print(f"Hello, {name}")

MyClass.greet("Bob")

b) @classmethod

A class method takes cls as the first argument and can be used to modify class state.

class MyClass:
    @classmethod
    def greet(cls, name):
        print(f"Hello, {name} from class {cls.__name__}")

MyClass.greet("Charlie")

5. Chaining Decorators

Multiple decorators can be applied to the same function. The decorators are applied bottom to top, meaning the decorator closest to the function is applied first.

Example: Chaining Decorators

def decorator_one(func):
    def wrapper():
        print("Decorator One")
        return func()
    return wrapper

def decorator_two(func):
    def wrapper():
        print("Decorator Two")
        return func()
    return wrapper

@decorator_one
@decorator_two
def hello():
    print("Hello, World!")

hello()

Output:

Decorator One
Decorator Two
Hello, World!

Here, decorator_two is applied first, and then decorator_one is applied to the resulting wrapped function.

6. Using Decorators for Function Memoization

One common use of decorators is for memoization, where results of expensive function calls are cached so that subsequent calls with the same arguments are faster.

Example: Memoization Decorator

def memoize(func):
    cache = {}
    def wrapper(*args):
        if args not in cache:
            print(f"Calculating result for {args}")
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def expensive_function(n):
    return n * n

print(expensive_function(4))
print(expensive_function(4))  # This time, it uses the cached result

Output:

Calculating result for (4,)
16
16

Here, the memoize decorator stores the results of function calls so that repeated calls with the same arguments do not need to be recomputed.

7. Using Decorators for Logging and Validation

Decorators are commonly used for tasks like logging and validation. For example, you can create a decorator that logs function calls or validates inputs before the function is executed.

Example: Logging Decorator

def log_function_call(func):
    def wrapper(*args, **kwargs):
        print(f"Function {func.__name__} was called with arguments {args} and keyword arguments {kwargs}")
        return func(*args, **kwargs)
    return wrapper

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

add(2, 3)

Output:

Function add was called with arguments (2, 3) and keyword arguments {}

8. Summary of Decorators

  • A decorator is a function that takes another function and modifies or enhances its behavior.
  • Decorators are useful for logging, access control, memoization, and other cross-cutting concerns.
  • Python's @decorator syntax is a shorthand for applying decorators to functions.
  • Decorators are applied in bottom to top order if multiple decorators are used.
  • Decorators can also accept arguments and be used with built-in decorators like @staticmethod and @classmethod.

Decorators are a powerful feature in Python that allow you to extend the functionality of existing code without modifying the original functions. They are commonly used in web development, data processing, and many other areas where additional functionality is needed for functions or methods.

Commenting is not enabled on this course.