What are Decorators?What are Decorators?

Let's discuss decorators.
Note that "Decorators" can be a challenging topic for beginners. We will examine decorators in detail, step by step, explaining how they work, but it may be challenging to comprehend. Are you prepared for the challenges? That's why you're here!

Note

The prerequisite topic for understanding decorators is closure. Therefore, make sure you understand how closure works before diving into decorators.

Decorators

Decorators are functions that add extra functionality to another function using the closure.

Code Description
This code demonstrates how to use a decorator to modify the behavior of a function without changing its code. Let's break down the code step by step:

  1. decorator(func) function:
  2. - This function takes another function func as an argument. The purpose of this function is to create and return a new function (a wrapper) that will execute some additional code before and after calling func.

  3. wrapper(argument1, argument2) function:
  4. - This is the wrapper function created inside the decorator() function. It takes two arguments, argument1 and argument2.
    - The wrapper() function starts by printing "Function starts executing" to indicate the beginning of the function's execution.
    - It then calls the original function func(argument1, argument2), where func is the function passed as an argument to the decorator().
    - After calling func, it prints "Function ends executing" to indicate the end of the function's execution.
    - The result of func(argument1, argument2) is stored in the variable result.
    - Finally, it returns the result.

  5. add(a, b) function:
  6. - This is the original function that we want to enhance with the decorator.
    - It takes two arguments, a and b.
    - It prints the message with the sum of a and b and returns their sum.

  7. add = decorator(add):
  8. - Here, we use the decorator() function to enhance the behavior of the add() function.
    - By assigning add = decorator(add), we replace the original add() function with the new version of add() that has the decorator applied to it. Now, whenever we call add(), we are actually calling the wrapper() function from the decorator.

  9. Function calls and output:
  10. - Now that the decorator is applied to the add() function, whenever we call add(a, b), it actually calls the wrapper() function instead.
    - The wrapper() function executes the additional code before and after calling the original add() function.
    - The output shows the messages from the wrapper() function and the result of the original add() function.

As you can see, the decorator successfully wraps the add() function, allowing us to add custom behavior to it without modifying its original code. Decorators are powerful tools in Python, used to add functionality, logging, or other cross-cutting concerns to functions or methods in a clean and reusable way.

Here is an example of how a decorator works. The decorator() function takes a function as an argument, defines the wrapper() function, encloses the taken function within the wrapper(), and returns the wrapper(). There are three steps to the decorator's work:

  • Taking a function as an argument.
  • Enclosing the function within the newly defined function (wrapper).
  • Returning the wrapper function with the enclosed function.

The wrapper() function contains the main decorator logic and invokes the function with the given parameters.

The add() function is reassigned by the returned wrapper() function that now contains the enclosed add() function.

Decorator Implementing

Step 1. Define the decorator.

The decorator should take exactly one argument.

Step 2. Define the inner function.

We need to define the inner function to close the function taken by decorator().

Step 3. Enclose the taken function.

The function should be called inside the inner function (wrapper), and the result should be saved and returned.

Step 4. Return the inner function.

The decorator should return the inner function wrapper without calling.

How does the decorator work?

Step 1: Decorator is called.

The decorator() function is called and takes the function (decorated function) as the argument func. At this step, the interpreter creates the decorator() local scope.

Step 2: Define the wrapper function.

The interpreter defines the wrapper() function (in the decorator() local scope) that takes the same arguments as the decorated function. The wrapper() body contains the main logic of the decorator and calls the function from the non-local scope.
The wrapper() function is not executed at this step.

Step 3: Decorator execution ends.

The decorator() function ends the execution and returns the wrapper() function. The interpreter removes the decorator() local scope but leaves the enclosed objects.

Step 4: Reassignment

The returned wrapper() function assigns to the decorated function: The decorated function is replaced by the other function (wrapper()), and the previously contained function is removed.

The previous add() function is removed but enclosed inside the returned wrapper() function.

Step 5: Usage

The new function is the returned wrapper() function that takes arguments and inserts them into the enclosed function.

Note

The add is a variable that contains a reference to the function that we defined.

Everything was clear?

Section 3. Chapter 1