Interview Questions, Answers and Tutorials

Decorators

Decorators

What are Decorators?

Imagine you have a sandwich. Sometimes, you want to add extra cheese or sauce to make it tastier. Decorators in Python are like those extra toppings you add to your sandwich. They are special tools that let you add extra features to your functions without changing the function itself.

Why Use Decorators?

Decorators help us:

  • Add extra functionality to our functions.
  • Keep our code clean and organized.
  • Reuse code by applying the same decorator to multiple functions.

How Do Decorators Work?

Let’s start with a simple example. Imagine we have a function that says hello:

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

Now, what if we want to add some excitement to our greeting? We can use a decorator for that.

Creating a Simple Decorator

A decorator is a function that takes another function as an argument, adds some extra features, and returns a new function. Here’s a simple decorator that adds excitement:

def add_excitement(func):
    def wrapper():
        print("Excited to say:")
        func()
        print("Yay!")
    return wrapper

Let’s break it down:

  • add_excitement(func): This is our decorator. It takes a function func as an argument.
  • def wrapper(): Inside the decorator, we define a new function called wrapper.
  • print("Excited to say:"): The wrapper function adds some excitement before calling the original function.
  • func(): The wrapper function calls the original function.
  • print("Yay!"): The wrapper function adds some more excitement after calling the original function.
  • return wrapper: The decorator returns the wrapper function.

Using the Decorator

Now, let’s use our decorator to add excitement to the say_hello function:

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

The @add_excitement line is how we apply the decorator to the say_hello function. When we call say_hello(), it will actually call the wrapper function inside the decorator.

say_hello()

Output:

Excited to say:
Hello!
Yay!

Practice Question 1

Create a decorator called shout that makes a function print its message in uppercase letters.

Solution:

def shout(func):
    def wrapper():
        result = func()
        print(result.upper())
    return wrapper

@shout
def greet():
    return "hello there"

greet()

Output:

HELLO THERE

Decorators with Arguments

Decorators can also take arguments. For example, let’s create a decorator that repeats the message a certain number of times.

def repeat(times):
    def decorator(func):
        def wrapper():
            for _ in range(times):
                func()
        return wrapper
    return decorator

Here’s how to use it:

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()

Output:

Hello!
Hello!
Hello!

Practice Question 2

Create a decorator called debug that prints the name of the function being called and its arguments.

Solution:

def debug(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with arguments {args} and {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@debug
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

Output:

Calling say_hello with arguments ('Alice',) and {}
Hello, Alice!

Summary

  • Decorators are like magic wrappers that add extra features to functions.
  • They help keep code clean and reusable.
  • Decorators can take arguments and handle multiple function arguments.

Practice Makes Perfect

Try creating your own decorators for fun tasks, like logging, timing, or even modifying return values. The more you practice, the more magical your code will become!


Happy coding! 🎉