(Python) decorator

Before understanding function decorators, let's first understand a development principle: the open and closed principle .

    After the software is launched, it must follow this principle, it is closed to modify the source code, and open to the function. In order to comply with this principle, we need to find a good solution to add new functions to it without modifying the source code of a function and its calling method.

A decorator is to add new functions to the decorated object without modifying the source code and calling method of the decorated object.

    Let's look at a small piece of code:

import time
def index():
    time.sleep(3)
    print('This is a small piece of code')
index()

    Now we have a problem. We need to add and calculate the running time of the index() function without modifying the source code and calling method.

We may immediately react to this and modify it:

import time
def index():
    start_time=time.time()
    time.sleep(1)
    print('This is a small piece of code')
    stop_time=time.time()
    print('Running time is: %s seconds' %(stop_time-start_time))

index()

Anyone with a discerning eye will know at a glance that although the calling method of your code has not changed and the functions have been increased, the source code has been modified! So no.

Then you think it's better to modify it like this:

import time
def index():
    time.sleep(3)
    print('This is a small piece of code')

start_time = time.time()
index()
stop_time = time.time()
print('Running time is: %s seconds' % (stop_time - start_time))

Looking at the above code, the source code of the index function has not been modified, the calling method has not changed, and functions have also been added, no problem. It should be fine. But at a glance from the tech giant, your code is too low. If you want to calculate the running time of other functions, you must copy the following three lines of code constantly:

start_time = time.time()
#index()Other functions
stop_time = time.time()
print('Running time is: %s seconds' % (stop_time - start_time))

Wouldn't that make the code redundant? The boss knew at a glance that your skills were too low and wanted to open you up.

So you think, let's be high-end, I'll make them into functions to call:

import time
def index():
    time.sleep(3)
    print('This is a small piece of code')

def wrapper(func):
        start_time= time.time()
        func()
        stop_time=time.time()
        print('Running time is: %s seconds' % (stop_time - start_time))
wrapper(index)
It seems that this code is a bit high-end, but you have changed the way the function is called. Completely violates the open closed principle. You still have to be fired by the boss, so you ask the tech boss, the boss says it's easy, you won't get numb if you use the closure function. At this time, you are confused. What is a closure function, you don't understand.

    The big guy said that a closure function is a function defined inside a function, and the function contains a reference to the name in the outer function scope . You are still confused when you hear it, so the big guy quickly typed the following code for you to understand:

def outter():
    name='monicx'
    def inner():
        print('my name is %s' %name)
    return inner
inner=outter()
inner()

From the above code, you can see some doorways. There is an inner() function nested in the outer() function. The inner() function is inside the outer() function. Generally, the inner function cannot be called from the outside, but You can see that the last line of code in the outer() function body returns the inner() namespace. So when you call outer(), you get the namespace of the inner function, so you treat it as a value and assign it to a variable name. So assign it to the variable name inner. By adding a () to this variable name, you can call the inner() function inside. And the value of the name variable within the scope of the outer() function can also be used by inner(). So you kind of understand the power of closure functions.

    Since you can't wait to use the knowledge points you just learned to optimize the low-force code you wrote before, you get the following code:

import time
def index():
    time.sleep(3)
    print('This is a small piece of code')
    

def auth(func):#func=the most original index
    def wrapper():
            start_time= time.time()
            func()
            stop_time=time.time()
            print('Running time is: %s seconds' % (stop_time - start_time))       
    return wrapper
index=auth(index)#新的index=wrapper

index()#wrapper()

The above code in the auth() function is executed when auth(index) starts to execute.

And pass in the index function namespace of the source code as a parameter. assign to func

Then the wrapper() function is defined in the auth() function body,

Then use the namespace of the wrapper() function as the return value of the call to the auth() function.

Assign this return value to the index variable, at this time the new index=wrapper

Because wapper is a function name, index is also a function name.

Therefore, the operation of the wrapper() function in the auth() function can be called through the new index().

Then execute to func(), func() is the index() function that executes the source code.

After such a series, you find that the index() of the call has been decorated with a function to calculate the running time of the index() function, and the code in the original index() function body has not been changed! ! ! Sigh such a decoration is really wonderful.

So you can't wait to decorate other functions with it.

import time
def index():
    time.sleep(3)
    print('This is a small piece of code')

def home(name):
    time.sleep(2)
    print('This is another %s code' %name)
#==============Decorator====================
def auth(func): # func=the original index
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('Running time is: %s seconds' % (stop_time - start_time))

    return wrapper

index = auth(index)  # 新的index=wrapper
home= auth(home)
#===============end=====================
index()  # wrapper()
home(name='moncix')

operation result:

Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/untitled/day20/test.py", line 58, in <module>
    home(name='moncix')
TypeError: wrapper() got an unexpected keyword argument 'name'
Here is a small piece of code
The runtime is: 3.000171422958374 seconds

How can there be a bug! ! ! ! Oh, it turns out that there was an error in the parameter transfer. index() is a function without parameters, and home(name) has parameters, so you think of using the knowledge of variable parameters ( *: to solve the actual parameters defined by position, **: to solve the definition by keyword parameter ), this decorator can be decorated with or without parameters. The improved code is as follows:

import time
def index():
    time.sleep(3)
    print('This is a small piece of code')

def home(name):
    time.sleep(2)
    print('This is another %s code' %name)
#==============Decorator====================
def auth(func): # func=the original index
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print('Running time is: %s seconds' % (stop_time - start_time))

    return wrapper

index = auth(index)  # 新的index=wrapper
home= auth(home)
#===============end=====================
index()  # wrapper()
home(name='moncix')

This code runs successfully! ! ! !

Here is a small piece of code
The runtime is: 3.000171661376953 seconds
Here is the code for another moncix
The runtime is: 2.0001144409179688 seconds

Later you found that decorators also have special syntactic sugar:

    First put the decorated function in front of the decorated function, and then add a line before the decorated function: @ decorate the function name.

import time
#==============Decorator====================
def auth(func): # func=the original index
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print('Running time is: %s seconds' % (stop_time - start_time))

    return wrapper
#===============end=====================
@auth #@auth is equal to index = auth(index)
def index():
    time.sleep(3)
    print('This is a small piece of code')

@auth#@auth is equal to home = auth(home(name))
def home(name):
    time.sleep(2)
    print('This is another %s code' %name)

index()
home(name='moncix')

The running result remains the same. You find that the syntax sugar of the decorator is really useful. When using it, you just need to use a special line to @ it on the decorated function.

Later, you wrote a lot of decorators, and you summed up a short and concise decorator template for those little whites to refer to:

Without the parameter decorator:

def outter(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

Comes with a parameter decorator:

def parameters(x, y, z):
    def outter(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return wrapper
    return outter

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325749217&siteId=291194637