SoFunction
Updated on 2025-03-01

Python In-depth Learning Decorator

Decorator is a high-level Python syntax. A decorator can process a function, method or class. In Python, we have many methods to process functions and classes. For example, in Python closures, we see the function object as the return result of a certain function. Compared with other methods, the decorator has simple syntax and high code readability. Therefore, decorators are widely used in Python projects.

Decorators first appeared in Python 2.5. They were originally used to process callable objects such as functions and methods (called objects defined by __call__ method). In Python 2.6 and later Python versions, decorators are further used for processing classes.

Decorative functions and methods

We first define two simple mathematical functions, one for calculating the sum of squares and the other for calculating the squared difference:

Copy the codeThe code is as follows:

# get square sum
def square_sum(a, b):
    return a**2 + b**2

# get square diff
def square_diff(a, b):
    return a**2 - b**2

print(square_sum(3, 4))
print(square_diff(3, 4))

After having basic mathematical functions, we may want to add other functions to the function, such as printing input. We can rewrite the function to achieve this:

Copy the codeThe code is as follows:

# modify: print input

# get square sum
def square_sum(a, b):
    print("intput:", a, b)
    return a**2 + b**2

# get square diff
def square_diff(a, b):
    print("input", a, b)
    return a**2 - b**2

print(square_sum(3, 4))
print(square_diff(3, 4))

We modified the definition of the function to add functions.

Now, we use the decorator to implement the above modification:

Copy the codeThe code is as follows:

def decorator(F):
    def new_F(a, b):
        print("input", a, b)
        return F(a, b)
    return new_F

# get square sum
@decorator
def square_sum(a, b):
    return a**2 + b**2

# get square diff
@decorator
def square_diff(a, b):
    return a**2 - b**2

print(square_sum(3, 4))
print(square_diff(3, 4))


Decorators can be defined in the form of def, such as the decorator in the above code. The decorator receives a callable object as an input parameter and returns a new callable object. The decorator has created a new callable object, which is the new_F above. In new_F, we add the printing function and implement the function of the original function by calling F(a, b).

After defining the decorator, we can use it through the @ syntax. Call @decorator before the functions square_sum and square_diff are defined, we actually pass square_sum or square_diff to the decorator and assign the new callable object returned by the decorator to the original function name (square_sum or square_diff). So, when we call square_sum(3, 4), it is equivalent to:

Copy the codeThe code is as follows:

square_sum = decorator(square_sum)
square_sum(3, 4)

We know that variable names and objects in Python are separated. The variable name can point to any object. In essence, the decorator plays a role of repointing the variable name (name binding), allowing the same variable name to point to a newly returned callable object, thereby achieving the purpose of modifying the callable object.

Similar to processing functions, we can use the decorator processing method.

If we have other similar functions, we can continue to call the decorator to modify the function without repeatedly modifying the function or adding new encapsulation. In this way, we improve the reusability of the program and increase the readability of the program.

Decorators with ginseng

In the above decorator call, such as @decorator, the decorator defaults to the function that follows it as the only parameter. The syntax of the decorator allows us to provide other parameters when calling the decorator, such as @decorator(a). This provides greater flexibility for the writing and use of decorators.

Copy the codeThe code is as follows:

# a new wrapper layer
def pre_str(pre=''):
    # old decorator
    def decorator(F):
        def new_F(a, b):
            print(pre + "input", a, b)
            return F(a, b)
        return new_F
    return decorator

# get square sum
@pre_str('^_^')
def square_sum(a, b):
    return a**2 + b**2

# get square diff
@pre_str('T_T')
def square_diff(a, b):
    return a**2 - b**2

print(square_sum(3, 4))
print(square_diff(3, 4))


The above pre_str is the decorator that allows parameters. It is actually a function encapsulation of the original decorator and returns a decorator. We can understand it as a closure containing environmental parameters. When we call using @pre_str('^_^'), Python can discover this layer of encapsulation and pass parameters to the decorator environment. This call is equivalent to:
Copy the codeThe code is as follows:

square_sum = pre_str('^_^') (square_sum)

Decoration

In the above example, the decorator receives a function and returns a function, thus achieving the effect of processing the function. After Python 2.6, decorators were expanded to classes. A decorator can receive a class and return a class, thus achieving the effect of processing the class.

Copy the codeThe code is as follows:

def decorator(aClass):
    class newClass:
        def __init__(self, age):
            self.total_display   = 0
                     = aClass(age)
        def display(self):
            self.total_display += 1
            print("total display", self.total_display)
            ()
    return newClass

@decorator
class Bird:
    def __init__(self, age):
        = age
    def display(self):
        print("My age is",)

eagleLord = Bird(5)
for i in range(3):
    ()


In the decorator we return a new class newClass. In the new class, we record the object() generated by the original class and attach the new property total_display to record the number of times the display is called. We also changed the display method at the same time.

By modifying, our Bird class can display the number of times it calls display.

Summarize

The core function of the decorator is name binding. This syntax is another embodiment of the Python multi-programming paradigm. Most Python users do not need to define decorators very much, but they may use decorators. Given the widespread use of decorators in Python projects, it is very beneficial to understand this syntax.