python decorator

Posted by adt2007 on Thu, 16 May 2019 21:36:01 +0200

The so-called decorator is a function, but its object of operation is other functions. It exists to add functions to some functions, but there are two restrictions when adding functions:

  1. Can't change the source code of the original function
  2. Can't change the way the original function is called

First of all, we need to understand some concepts before we can understand the decorator.

Variable definition

Unlike many languages, python does not need to declare variables when defining them, but assigns them directly, such as

a = 2
b = 2
print a,b
2 2
b = 3
print a,b
2 3

When we do "a = 2", we create a "2" object in memory and assign the address of the object "2" to a. If we continue to "b = 2", python will not continue to create a "2" object, but assign the address of "2" to B directly, and the counter of "2" object is added to 1 (which involves python memory management). Some concepts, without further elaboration, if we continue with "b = 3", we create another "3" object, and then assign the address to b, which does not change a. That's a bit confusing. Let's see the picture.

Definition of function

The definition of functions in python uses the keyword "def":

def function():
    print "this is an function"

function()
this is an function


Is the definition of sensory function very similar to the definition of variables? Yes, in fact, there is no big difference between defining a function and defining a variable. The function name is equivalent to our variable name. As we all know, variables can be passed to functions as parameters, so can function names be passed to other functions as parameters? Don't worry, keep looking down.

Function name as parameter

def function1():
    print "this is an function1"
def function2(fuc):
    fuc()
    print "this is an function2"
function2(function1)

#Operation result
this is an function1
this is an function2

See no, function name can be passed as a parameter. In the code above, we pass "function 1" as a parameter to "function 2". In "function 2", calling "function 1" by function name can also realize the function of "function 1".
As for "function 2", we have not only realized the function of "function 1", but also realized some functions that "function 1" does not have. Have we added some functions on the basis of not modifying the source code of "function 1"? Have we, have we, say have we!!!?
So is this decoration? Congratulations, the answer is wrong! A lot of sharp-eyed friends must have found out that in the first two requirements, only one requirement is satisfied: "do not modify the source code of the function"; the method of calling the function has changed, formerly "function 1 ()", now it has become "function 2 ()". How to achieve this, let's uncover the true face of Lushan decorator!!!

Simple decorator

def function1():
    print "this is an function1"
def decorat(func):
    def function2():
        func()
        print "this is an function2"
    return function2
function1 = decorat(function1)
function1()

#Operation result
this is an function1
this is an function2

What earth-shaking things have we all done in this code? Let's have a look.

  • First, we define a simple function "function 1"
  • Then we define a "decorat" function with a parameter "func". We nest a "function 2" function in the "decorat" function. In the function body of "function 2", we call "func ()" here. It should not be difficult for you to see that the parameter "func" passed in before should be a function name. After the call, we can see that "func" should be a function name. We also add a bit of other code (other functions you want to add), define "function 2", and return the function name "function 2" as a result of "decorat"
  • Next, we call the "decorat" function through the statement "function 1 = decorat (function 1)", is it equivalent to passing "function 1" as "func" to the function "decorat", and then assigning the return value of "decorat" to "function 1"?
  • Finally, we call "function 1 ()". Is "function 1" still the "function 1" we just defined? The answer is definitely not. At this time, "function 1" has been replaced by "function 2" returned. When we call "function 1 ()", we call "function 2 ()".
  • In this way, without modifying the source code of the original function and the way it is called, we quietly add some functions that we want to add to it. Is it amazing?

But according to this operation, every decorated function needs a statement like "function 1 = decorat (function 1)". Is it troublesome and does not conform to python's concise style, so Python provides a very simple method: @decorat?

def decorat(func):
    def function2():
        func()
        print "this function was decorated"
    return function2
@decorat
def function1():
    print "this is an function1"
@decorat
def function2():
    print "this is an function2"
function1()
function2()

#Operation result
this is an function1
this function was decorated
this is an function2
this function was decorated

Just add a @ decorat statement next to each function you need to decorate. This is also called grammatical sugar. Like sugar, it sticks to the front of the function. Here, decorat is your own decorator. That is to say, @decorat"is equivalent to"function = decorat(function)"

Decorated function band parameters

The previous decorated functions are all parametric and belong to the simplest form. Next, let's look at how to decorate the functions with parameters.

def decorat(func):
    def function2(name):
        func(name)
        print "this function was decorated"
    return function2

@decorat
def function1(name):
    print "this is an function1"
    print "my name is "+name
@decorat
def function2(name):
    print "this is an function2"
    print "my name is " + name

function1("Tom")
function2("Alice")

#Operation result
this is an function1
my name is Tom
this function was decorated
this is an function2
my name is Alice
this function was decorated

In this example, it is not difficult to see that I have added a "str" type parameter to the decorated function, and it has been decorated successfully. How can this be achieved? In fact, it's not difficult. Careful companions may have found it, as long as the function we embedded in the decorator contains the same parameters. But this also caused a trouble, can not achieve the decoration of parametric function, here we teach you a little skill, variable parameters.

def decorat(func):
    def function2(*args,**kwargs):
        func(*args,**kwargs)
        print "this function was decorated"
    return function2

@decorat
def function1(name):
    print "this is an function1"
    print "my name is "+name
@decorat
def function2():
    print "this is an function2"
    print "this function do not have name"

function1("Tom")
function2()

#Operation result
this is an function1
my name is Tom
this function was decorated
this is an function2
this function do not have name
this function was decorated

Decorator with parameters

Before I said how to decorate the function with parameters, some people may ask, can the decorator also take parameters? To decorate some functions differently, how many functions can a decorator achieve? The answer is yes. Let's not say much. There's nothing more tangible than code.

import time
def decorat(mult):
    def wrapp(func):
        def function2(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            end_time = time.time()
            wast_time = end_time - start_time
            print mult,"Double wast_time =",wast_time*mult
            print "this function was decorated-------------------\n"
        return function2
    return wrapp

#If I want to compute the running time of function1 and print it
#And twice the run time of printing function 2
@decorat(1)
def function1(name):
    print "this is an function1"
    time.sleep(3)
    print "my name is "+name
@decorat(2)
def function2():
    print "this is an function2"
    time.sleep(3)
    print "this function do not have name"

function1("Tom")
function2()

#Operation result
this is an function1
my name is Tom
1 Double wast_time = 3.0150001049
this function was decorated-------------------

this is an function2
this function do not have name
2 Double wast_time = 6.04199981689
this function was decorated-------------------

Through this example, you can see that the decorator can take parameters; but the decorator with parameters needs to nest a multiple function to receive the parameters that you call the decorator. The "wrapp" in this code is actually equivalent to the "decorat" in the previous code to receive the name of your decorated function, and here the "decorat" is used. To receive the parameters that come in when you call the decorator.

Call order of decorator

Decorators are invoked in the reverse order of grammatical sugar writing.

import time
def decorat2(func):
    def wrapp():
        func()
        print "this is decorat2"
    return wrapp

def decorat(mult):
    def wrapp(func):
        def function2(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            end_time = time.time()
            wast_time = end_time - start_time
            print mult,"Double wast_time =",wast_time*mult
            print "this function was decorated-------------------\n"
        return function2
    return wrapp

@decorat2
@decorat(2)
def function2():
    print "this is an function2"
    time.sleep(3)
    print "this function do not have name"

function2()

#Operation result
this is an function2
this function do not have name
2 Double wast_time = 6.00600004196
this function was decorated-------------------

this is decorat2

Summary: Here is a brief description of some decorative concepts and use, I have just learned, if there are any shortcomings and incorrect places, you are welcome to correct, exchange.

Topics: Python