What are decorators in Python and how do they work

Umesh S
4 min readFeb 21, 2023

--

Photo by Pankaj Pandey on Unsplash

Decorators in Python are a powerful feature that allows modifying the behavior of a function or a class without changing its source code. They provide a way to wrap a function or a class with another function or class, allowing the addition of new functionalities or changing the way the original function or class works.

The syntax for using decorators is to place the “@” symbol followed by the name of the decorator function above the function that is to be decorated. When the decorated function is called, the decorator function is also called, and it can perform some actions before and/or after the call to the decorated function.

Here is an example of a decorator function that takes a function as an argument, prints a message before the function is called, calls the function, and prints a message after the function returns:

def my_decorator(func):
def wrapper():
print("Before the function is called.")
func()
print("After the function is called.")
return wrapper

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

say_hello()

When the say_hello function is called, it is wrapped by the my_decorator function. The wrapper function is returned and assigned to the say_hello function name. So when say_hello is called, it actually calls the wrapper function, which first prints "Before the function is called.", then calls the original say_hello function that prints "Hello!", and finally prints "After the function is called."

Decorators can be used for a variety of purposes, such as logging, measuring performance, authentication, memoization, and more. Decorators can also be chained together by applying multiple decorators to a single function or class.

def my_decorator1(func):
def wrapper():
print("Before my_decorator1 is called.")
func()
print("After my_decorator1 is called.")
return wrapper

def my_decorator2(func):
def wrapper():
print("Before my_decorator2 is called.")
func()
print("After my_decorator2 is called.")
return wrapper

@my_decorator1
@my_decorator2
def say_hello():
print("Hello!")

say_hello()

In this example, the say_hello function is decorated with both my_decorator1 and my_decorator2. The decorators are applied in the order they are listed, so the output will be:

Before my_decorator1 is called.
Before my_decorator2 is called.
Hello!
After my_decorator2 is called.
After my_decorator1 is called.

In addition to function decorators, Python also has class decorators, which are used to modify the behavior of a class or its methods. Here’s an example of a class decorator that adds a new method to a class:

def my_class_decorator(cls):
class NewClass(cls):
def new_method(self):
print("This is a new method.")

return NewClass

@my_class_decorator
class MyClass:
def old_method(self):
print("This is an old method.")

my_obj = MyClass()
my_obj.old_method()
my_obj.new_method()

In this example, the my_class_decorator function takes a class as an argument and returns a new class that inherits from the original class but adds a new method. The @my_class_decorator decorator is applied to the MyClass class, which results in a new class NewClass that has the old_method inherited from MyClass and the new_method added by the decorator. When the old_method and new_method are called on an instance of MyClass, they will execute as expected.

Finally, Python also supports parameterized decorators, which allow you to pass arguments to the decorator function. This can be useful when you want to apply different behaviors to different functions or classes. Here’s an example of a parameterized decorator:

def my_decorator(arg1, arg2):
def inner_decorator(func):
def wrapper(*args, **kwargs):
print("Decorator arguments:", arg1, arg2)
func(*args, **kwargs)
return wrapper
return inner_decorator

@my_decorator("Hello", 42)
def my_function():
print("Inside the function.")

my_function()

In this example, the my_decorator function takes two arguments and returns an inner decorator function. The inner decorator takes a function as an argument and returns a wrapper function that prints the decorator arguments and then calls the original function. The @my_decorator("Hello", 42) decorator is applied to the my_function function, passing the arguments "Hello" and 42 to the decorator. When my_function is called, it will execute the wrapper function returned by the decorator, which will print the decorator arguments and then call the original function.

Parameterized decorators can be used to implement more complex behaviors, such as caching, logging, and input validation, among others. They provide a flexible and powerful mechanism for modifying the behavior of functions and classes in a modular way, making it easier to maintain and evolve software over time.

It’s also worth noting that decorators can be stacked, meaning that you can apply multiple decorators to a single function or class. When multiple decorators are used, they are applied from the bottom up, with the innermost decorator applied first. Here’s an example:

def my_decorator1(func):
def wrapper(*args, **kwargs):
print("Inside decorator 1.")
func(*args, **kwargs)
return wrapper

def my_decorator2(func):
def wrapper(*args, **kwargs):
print("Inside decorator 2.")
func(*args, **kwargs)
return wrapper

@my_decorator1
@my_decorator2
def my_function():
print("Inside the function.")

my_function()

In this example, the my_decorator1 and my_decorator2 decorators are applied to the my_function function. When my_function is called, the my_decorator2 decorator is applied first, printing "Inside decorator 2." and then calling the my_decorator1 decorator, which in turn prints "Inside decorator 1." and then calls the original function, which prints "Inside the function." The output of this code will be:

Inside decorator 1.
Inside decorator 2.
Inside the function.

Overall, Decorators are a powerful and flexible feature of Python that enable you to modify the behavior of functions and classes in a clean and modular way. They can be used to implement a wide variety of behaviors, such as caching, logging, input validation, and much more. By mastering the use of decorators, you can become a more effective and efficient Python developer.

--

--

Umesh S
Umesh S

Written by Umesh S

Experienced Software Engineer committed to helping others grow and succeed.

No responses yet