1. What are Decorators?

In Python, a decorator is a function that takes another function as its argument and returns a new function. The new function can be used in place of the original function, with additional functionality provided by the decorator. This allows you to modify or enhance the behavior of a function, without changing its source code.

A decorator can be defined using the @ symbol, followed by the name of the decorator function. For example:

@decorator
def my_function():
    # function code

 Here, decorator is the name of the decorator function, and my_function is the name of the function being decorated.

2. Types of Decorators

There are two types of decorators in Python: function decorators and class decorators. Function decorators are used to modify the behavior of a function, while class decorators are used to modify the behavior of a class.

2.1. Function Decorators

Function decorators are defined as functions that take another function as an argument and return a new function. The new function can be used in place of the original function, with additional functionality provided by the decorator.

Here's an example of a function decorator:

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()

In this example, my_decorator is a function that takes another function func as an argument and returns a new function wrapper. The wrapper function adds some extra functionality to the original function by printing a message before and after it is called. The @my_decorator syntax is used to apply the decorator to the say_hello function.  

2.2. Class Decorators

Class decorators are used to modify the behavior of a class. They are defined as functions that take a class as an argument and return a new class.

Here's an example of a class decorator:

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

@my_decorator
class MyClass:
    def my_method(self):
        print("This is my method.")

obj = MyClass()
obj.my_method()
obj.new_method()

In this example, my_decorator is a class decorator that takes a class cls as an argument and returns a new class NewClass. The NewClass class extends the original class by adding a new method new_method. The @my_decorator syntax is used to apply the decorator to the MyClass class.

3. Conclusion

Decorators are a powerful tool in Python that allow you to modify or enhance the functionality of functions or methods, without modifying their source code. There are two types of decorators in Python: function decorators and class decorators. Function decorators are used to modify the behavior of a function, while class decorators are used to modify the behavior of a class. With decorators, you can add new functionality to existing code, make your code more efficient, and improve its readability.