This is the third day of my participation in the August More text Challenge. For details, see:August is more challenging

The Python decorator understands

function

The main function of a decorator is to extend functionality for a class, method, etc., without changing existing code

Let’s start with a piece of code like this

def foo() :
    for i in range(10) :print(i)
foo()
Copy the code
Add logs that need to be printed

Now you need to calculate the running time log for this code without changing it

import time


def log(fun) :
    print('Start time :{}'.format(time.time()))
    fun()
    print('End time :{}'.format(time.time()))


def foo() :
    for i in range(10) :print(i)


log(foo)

Copy the code
Optimize the call mode to handle multiple calls

We passed foo as an argument to log(). In def log(fun), fun() is the same as foo(). Ok, now we can show the running time log, but this changes the way we call it, we need to use log(foo) to call it, Let’s say our code used foo() 100 times and now we need to print every run log of foo(). It’s not realistic to change everything so we need to optimize it

import time


def log(fun) :
    def run_log() :
        print('Start time :{}'.format(time.time()))
        fun()
        print('End time :{}'.format(time.time()))
    
    return run_log

def foo() :
    for i in range(10) :print(i)

foo = log(foo)
foo()


Copy the code

Now, when foo = log(foo), we call log(), internally define a run_log() method, and return it to foo, where foo is equal to run_log, and calling foo is equivalent to calling run_log, so that we can print the log. You don’t have to change existing code

The ginseng

The current foo() method can print numbers from 0 to 9, but due to a change in business requirements, we now need to pass a value to foo(), such as foo(100), to print numbers from 0 to 99, as well as print the log, so we need to optimize the code again

import time


def log(fun) :
    def run_log(num) :
        print('Start time :{}'.format(time.time()))
        fun(num)
        print('End time :{}'.format(time.time()))

    return run_log


def foo(num) :
    for i in range(num):
        print(i)


foo = log(foo)
foo(100)

Copy the code

We already know from previous logic that the new foo() is equal to run_log(), so when we pass a value of 100 to foo(100), it is actually equal to run_log(100), so we can’t accept it directly in def foo(num). We need to take arguments in run_log(num) and pass them to fun(num) so that the new function can take arguments

Returns the parameter

The new requirement is to return the sum of all the numbers in foo(), and our existing foo function is actually run_log, so let’s change the code again

import time


def log(fun) :
    def run_log(num) :
        print('Start time :{}'.format(time.time()))
        info = fun(num)
        print('End time :{}'.format(time.time()))
        return info

    return run_log


def foo(num) :
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num


foo = log(foo)
x = foo(100)
print(x)

Copy the code

Since foo() is now equal to run_log(), and fun() in run_log() is equal to foo(), the value returned in foo is passed to info and then we return info, so x can accept the argument passed from run_log

Ok, so here we have implemented a castrated version modifier

generality

If you need to print a log to a new function foo2, the code looks like this

import time


def log(fun) :
    def run_log(num) :
        print('Start time :{}'.format(time.time()))
        info = fun(num)
        print('End time :{}'.format(time.time()))
        return info

    return run_log


def foo(num) :
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num


def foo2(num, num2) :
    add_num = 0
    for i in range(num, num2):
        add_num += i
        print(i)
    return add_num


foo = log(foo)
x = foo(100)
print(x)

Copy the code

Since our decorator can only accept one parameter and foo2 needs two, the existing code can’t do that, so we’ll continue to upgrade the code

import time


def log(fun) :
    def run_log(*args,**kwargs) :
        print('Start time :{}'.format(time.time()))
        info = fun(*args,**kwargs)
        print('End time :{}'.format(time.time()))
        return info

    return run_log


def foo(num) :
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num


def foo2(num, num2) :
    add_num = 0
    for i in range(num, num2):
        add_num += i
        print(i)
    return add_num


foo = log(foo)
foo2 = log(foo2)
x = foo(100)
x2 = foo2(50.100)
print(x)
print(x2)

Copy the code

We used *args so that we could accept any number of parameters, which would satisfy all our needs

Syntactic sugar

However, having to write an assignment like foo = log(foo) before each use is not pretty code for Python, so Python provides some syntactic sugar. You can use @log instead

import time


def log(fun) :
    def run_log(*args,**kwargs) :
        print('Start time :{}'.format(time.time()))
        info = fun(*args,**kwargs)
        print('End time :{}'.format(time.time()))
        return info

    return run_log

@log
def foo(num) :
    add_num = 0
    for i in range(num):
        add_num += i
        print(i)
    return add_num

@log
def foo2(num, num2) :
    add_num = 0
    for i in range(num, num2):
        add_num += i
        print(i)
    return add_num

x = foo(100)
x2 = foo2(50.100)
print(x)
print(x2)

Copy the code

We only need to define the function by adding the @ modifier name on top of it to complete the function name = modifier name (function name)

It’s now a standard decorator

extension

Similarly, we can write a decorator in JS

function log(fun) {
	function run_log(. ags) {
		console.log('= = = = = = = = = = = = = =')
		letinfo = fun(... ags)console.log('= = = = = = = = = = = = = =')
		return info
	}

	return run_log
}


foo = log(foo)
function foo(num, num1) {
	let x = 0
	for (let i = num; i < num1; i++) {
		console.log(i)
		x += i
	}
	return x
}

foo2 = log(foo2)
function foo2(num) {
	let x = 0
	for (let i = 0; i < num; i++) {
		console.log(i)
		x += i
	}
	return x
}


foo(10.100)
foo2(10)

Copy the code