decorator USES instances in Python

  • 2020-05-09 18:49:37
  • OfStack

decorator has already been introduced in Blog, which I introduced earlier with Python 2.4 features. However, it was a bit of a copy-cat at the time, but now I'll describe its use in more detail.

The detailed introduction of decorator is already introduced in What's new in Python 2.4, you can have a look at 1.

How do I call decorator

There are basically two forms of calling decorator

1 species:


@A
def f ():

This form is written as decorator without arguments. Finally, Python will be processed as follows:


f = A(f)

It can also be expanded to:

@A
@B
@C
def f ():
   

Finally, Python will be processed as:


f = A(B(C(f)))

Note: the document says "@A" @B "@C", but it doesn't work. And the order of execution is in the order of function calls, first C at the bottom, then B, then A. Therefore, if decorator has a sequence, 1 must be careful: the first to be executed is placed at the bottom, and the last to be executed at the top. (this reverse order relationship should not exist.)

The second:


@A(args)
def f ():
   

This form is the parametric version of decorator. Then Python will be processed as:


def f():
_deco = A(args)
f = _deco(f)

As you can see, Python will first execute A(args) to get an decorator function, and then process it in the same way as the first one.

Definition of the decorator function

Each of the decorator pairs has a corresponding function, which handles the subsequent functions, returning either the original function object or a new function object. Note that decorator is only used to handle function and class methods.

1 species:
For the first form of invocation


def A(func):
    # To deal with func
    # Such as func.attr='decorated'
    return func
@A
def f(args):pass

Above is the processing of func, still return the original function object. The parameter of the decorator function is the function to be processed. If you want to return a new function, you can:


def A(func):
    def new_func(args):
        # do 1 Some extra work
        return func(args) # Call the function to continue processing
    return new_func
@A
def f(args):pass

Note that new_func is defined in the same form as the function to be processed, so it can also be written as generic 1, such as:


def A(func):
    def new_func(*args, **argkw):
        # do 1 Some extra work
        return func(*args, **argkw) # Call the function to continue processing
    return new_func
@A
def f(args):pass

As you can see, a new function is defined in A, and then A returns this new function. In a new function, do something first, such as checking the parameters, or do something else, and then call the original function. This pattern can be seen as some processing before the function is called, using decorator technology. If you want to do a little bit of processing after calling the function, or a little bit of processing after calling the function, based on the return value of the function, you can write something like this:


def A(func):
    def new_func(*args, **argkw):
        result = func(*args, **argkw) # Call the function to continue processing
        if result:
            # do 1 Some extra work
            return new_result
        else:
            return result
    return new_func
@A
def f(args):pass

The second:
For the second form of invocation

On the documentation, if your decorator is called with arguments, then your decorator function will only be called with those arguments, so you will need to return a new decorator function, which is identical to the first form, 1.


f = A(f)
0

You can see that A(arg) returns a new decorator _A.

Application scenarios of decorator

But I have been wondering, what is the magic of decorator? For what occasions? Do I need to use it?

The magic of decorator is that it can manipulate the functions it modifies. So the processing is done without changing the original function code. Kind of like I know so 1 bit of AOP(aspect oriented programming) idea.

I can think of a list of appropriate situations:

1. As stated in the documentation, it was originally intended to make it easier to call methods like staticmethod and classmethod
2. Do some work before some functions are executed. For example, in the development of web, many functions need to check whether the user is logged in before they can be called
3. Do some work after the execution of this function, such as after the completion of the call, write a log according to the return state
4. Do parameter checks

There may be many more, but you are free to imagine them

Do I need it then?

I guess it's up to you. However, I think that in some cases, using decorator can increase the flexibility of the program and reduce the coupling. Such as the user login check mentioned earlier. It is possible to write a generic login check function and call it in each function. But this makes the function less flexible and increases the degree of integration with other functions. If the user login check function is modified, such as the determination of the return value, it is possible that every function that USES it will be modified. Using decorator does not cause this problem. Using decorator's syntax also makes the code simple and clean (1 if you're familiar with its syntax). Of course you can do without it. However, this way of combining the functions is more in line with the requirements of building blocks. It can decompose the functions into 1 step, making the functions simple and simple enough. And then through the decorator mechanism to flexibly string the relevant functions into a string, which is good to think of as 1. For example:


@A
@B
def account(args):pass

Let's say this is an accounting function, account just accounts. But a real bookkeeping also involves some judgment and processing, such as: B checks account status, A logs. The effect is to check B first, then execute account by processing it in A, and then log it. Like building blocks, it is very convenient and easy to change. You can even write decorator as account, and the function that is executed below is an empty function. Then, through configuration files and other methods, the combination of decorator is saved to basically realize the assembly of functions. Isn't that ideal?

The creativity brought by Python is really boundless!


Related articles: