Python Decorators

Python decorators are a powerful tool for modifying or enhancing the behavior of functions or methods. In this guide, we will go through the fundamental concepts and provide examples of decorators in Python.



What Are Python Decorators?

A Python decorator is a function that modifies or enhances the behavior of another function. Decorators allow you to add functionality to an existing function without modifying its code. They are used to wrap functions with additional behavior.

Decorators are commonly used in situations where we need to apply the same logic across multiple functions (such as logging, validation, or authentication).


How Do Decorators Work?

A decorator works by taking a function as an argument, modifying it, and then returning a new function that adds functionality to the original one. This allows you to enhance the behavior of the function without changing its code.

Here’s an example that shows how decorators work:

python
def my_decorator(func):
    def wrapper():
        print("Before the function runs.")
        func()
        print("After the function runs.")
    return wrapper

def greet():
    print("Hello, World!")

greet = my_decorator(greet)
greet()

How It Works:

  • my_decorator(func) defines a decorator function that takes another function as its argument.
  • wrapper() adds behavior before and after calling func().
  • greet = my_decorator(greet) applies the decorator to the greet function.
  • greet() now runs the decorated version, printing messages before and after the original greeting.

Output:

Before the function runs.
Hello, World!
After the function runs.

Basic Structure of a Decorator

A decorator consists of two main components:

  1. The decorator function itself, which accepts another function as a parameter.
  2. The wrapper function that modifies or enhances the behavior of the original function.

Here’s the basic structure of a decorator:

python
def decorator_name(func):
    def wrapper():
        # Add functionality here
        return func()
    return wrapper

How Does the @ Symbol Work?

The @ symbol** is shorthand for applying a decorator to a function. Instead of manually passing the function through the decorator, you can use the `@decorator_name` syntax to apply the decorator directly above the function definition.

This is equivalent to writing:

python
greet = my_decorator(greet)

But with the `@` symbol, it’s cleaner and easier to read:

python
@my_decorator
def greet():
    print("Hello, World!")

A Decorator with Arguments

If the function being decorated accepts arguments, the decorator must also be able to accept and pass those arguments to the original function.

Here’s an example where the decorator accepts and passes arguments to the decorated function:

python
def decorator_with_args(func):
    def wrapper(*args, **kwargs):
        print("Arguments received:")
        print("Positional:", args)
        print("Keyword:", kwargs)

        return func(*args, **kwargs)
    return wrapper

@decorator_with_args
def greet(name, age):
    print(f"Hello {name}, you are {age} years old!")

greet("Jack", 30)

How It Works:

  • decorator_with_args(func) defines a decorator that accepts arguments and passes them to the original function.
  • wrapper(*args, **kwargs) handles the arguments and prints them out before calling the decorated function.
  • greet("Jack", 30) calls the decorated function, showing how the decorator captures and displays the arguments.

Output:

Arguments received:
Positional: ('Jack', 30)
Keyword: {}
Hello Jack, you are 30 years old!

Returning Values from Decorated Functions

Decorators can also modify or manipulate the return value of the function they decorate. The wrapper function can return a value, just like the original function.

Here’s an example where the decorator modifies the return value:

python
def return_value_decorator(func):
    def wrapper():
        result = func()
        return f"Modified: {result}"
    return wrapper

@return_value_decorator
def greet():
    return "Hello, World!"

print(greet())

How It Works:

  • return_value_decorator(func) defines a decorator that modifies the return value of the original function.
  • wrapper() calls the decorated function, captures its result, and modifies it before returning.
  • @return_value_decorator applies the decorator to the greet() function.
  • greet() now returns the modified string instead of the original one.

Output:

Modified: Hello, World!

Chaining Multiple Decorators

You can apply multiple decorators to a function. They will be executed in the order they are listed, with the decorator closest to the function being applied first.

Here’s an example of chaining multiple decorators:

python
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 greet():
    print("Hello, World!")

greet()

How It Works:

  • decorator_one(func) and decorator_two(func) are two decorators that modify the behavior of the greet function.
  • Decorators are applied in a bottom-up manner, so decorator_two is applied first, followed by decorator_one.
  • The greet function is decorated by both, and each decorator prints a message before executing the function.

Output:

Decorator One
Decorator Two
Hello, World!

Frequently Asked Questions

What is a decorator in Python?

A decorator in Python is a function that modifies or enhances the behavior of another function without changing its code. It's commonly used for logging, authentication, or adding functionality to functions in a clean and reusable way.


How do decorators work in Python?

Decorators work by taking a function as an argument, wrapping it inside another function (usually called a wrapper), and then returning the new function. This allows you to modify the original function’s behavior without altering its code.


What is the purpose of the @ symbol in Python decorators?

The @ symbol is used as a shorthand to apply a decorator to a function. It is placed directly above the function definition, making the code cleaner and more readable.


Can decorators accept parameters?

Yes! Decorators can accept parameters, which allows you to customize how the decorator behaves. These parameters are passed when the decorator is applied to a function.


Can you chain multiple decorators in Python?

Yes, you can apply multiple decorators to a function. They will be executed in the order in which they are applied, with the decorator closest to the function being applied first.



What's Next?

Now that you’ve learned about decorators, the next step is to dive into Python's powerful Regex (Regular Expressions). We'll explore how to use regex in Python for pattern matching, searching, and text manipulation — an essential skill for working with strings efficiently.