Pyhton closures, decorators

Keywords: Python Lambda Attribute Database

closure

1. Function Reference

def test1():
    print("--- in test1 func----")

# Call function
test1()

# Reference function
ret = test1

print(id(ret))
print(id(test1))

#Call functions by reference
ret()

Operation results:

--- in test1 func----
140212571149040
140212571149040
--- in test1 func----

2. What is closure

# Define a function
def test(number):

    # Define a function inside the function, and the function uses the variables of the outer function. Then call this function and some variables used closure.
    def test_in(number_in):
        print("in test_in function, number_in is %d" % number_in)
        return number+number_in
    # In fact, what is returned here is the result of the closure.
    return test_in


# To assign a value to the test function, this 20 is to give the number of parameters.
ret = test(20)

# Note that 100 here actually gives the parameter number_in
print(ret(100))

#Note that 200 here actually gives the parameter number_in
print(ret(200))

Operation results:

in test_in function, number_in is 100
120

in test_in function, number_in is 200
220

3. Look at a practical example of closures:

def line_conf(a, b):
    def line(x):
        return a*x + b
    return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))

In this example, the function line and variables A and B form closures. When creating closures, we illustrate the values of these two variables through the parameter a and B of line_conf, so that we determine the final form of the function (y = x + 1 and y = 4x + 5). We can obtain different linear expression functions by changing parameters a and B. From this, we can see that closures can also improve code reusability.

If there are no closures, we need to specify a,b,x every time we create a linear function. In this way, we need more parameter passing, which also reduces the portability of the code.

Note:

Because closures refer to local variables of external functions, local variables of external functions are not released in time and memory is consumed.

4. Modify variables in external functions

Python 3 method

def counter(start=0):
    def incr():
        nonlocal start
        start += 1
        return start
    return incr

c1 = counter(5)
print(c1())
print(c1())

c2 = counter(50)
print(c2())
print(c2())

print(c1())
print(c1())

print(c2())
print(c2())

Python 2 method

def counter(start=0):
    count=[start]
    def incr():
        count[0] += 1
        return count[0]
    return incr

c1 = closeure.counter(5)
print(c1())  # 6
print(c1())  # 7
c2 = closeure.counter(100)
print(c2())  # 101
print(c2())  # 102

Decorator

Decorator is a function that is often used in program development. With good use of decorator, development efficiency is like a tiger's wings. So this is also a question that Python interviews must ask. But for many people who have first contacted this knowledge, this function is somewhat circumvented. When they study by themselves, they go around directly, and then they hang up when they come to the interview. Because decorator is the basic knowledge of program development, this will not be. Don't tell people that you know Python, read the following article, to ensure that you learn decorator.

1. Understand this code first

#### first wave ####
def foo():
    print('foo')

foo  # Representation is a function
foo()  # Represents execution of foo functions

#### Second Wave ####
def foo():
    print('foo')

foo = lambda x: x + 1

foo()  # Execute the lambda expression instead of the original foo function, because the name foo is redirected to another anonymous function

The function name is only a variable, but it points to the defined function, so it can be called through the function name (). If the function name = xxx is modified, then when the function name () is executed, the called function is unknown.

2. Demand is coming

Start-up companies have N business departments, the basic platform department is responsible for providing the underlying functions, such as: database operations, redis calls, monitoring API and other functions. When business departments use basic functions, they only need to call the functions provided by the basic platform. As follows:

############### The basic platform provides the following functions ###############

def f1():
    print('f1')

def f2():
    print('f2')

def f3():
    print('f3')

def f4():
    print('f4')

############### Business Departments A Call the functions provided by the underlying platform ###############

f1()
f2()
f3()
f4()

############### Business Departments B Call the functions provided by the underlying platform ###############

f1()
f2()
f3()
f4()

At present, the company is in an orderly way, but in the past, the developers of the basic platform did not pay attention to validation when writing code, that is, the functions provided by the basic platform can be used by anyone. Now it is necessary to reconstruct all the functions of the basic platform and add verification mechanism for all the functions provided by the platform, that is, validation before executing the functions.

The boss gave the job to Low B, and he did so:
Negotiate with each business department. Each business department writes its own code and verifies before calling the functions of the underlying platform. In this way, the underlying platform does not need to be modified. That's great. There's plenty of time to pick up girls.

Low B was fired that day.

The boss gave the job to Low BB, and he did so:

############### The basic platform provides the following functions ############### 

def f1():
    # Verification 1
    # Verification 2
    # Verification 3
    print('f1')

def f2():
    # Verification 1
    # Verification 2
    # Verification 3
    print('f2')

def f3():
    # Verification 1
    # Verification 2
    # Verification 3
    print('f3')

def f4():
    # Verification 1
    # Verification 2
    # Verification 3
    print('f4')

############### Business Departments remain unchanged ############### 
### Business Departments A Call the functions provided by the underlying platform### 

f1()
f2()
f3()
f4()

### Business Departments B Call the functions provided by the underlying platform ### 

f1()
f2()
f3()
f4()

A week later Low BB was fired.

The boss gave the job to Low BBB, and he did so:
Only refactoring the code of the underlying platform without any modification by other business departments

############### The basic platform provides the following functions ############### 

def check_login():
    # Verification 1
    # Verification 2
    # Verification 3
    pass


def f1():

    check_login()

    print('f1')

def f2():

    check_login()

    print('f2')

def f3():

    check_login()

    print('f3')

def f4():

    check_login()

    print('f4')

The boss looked at the realization of Low BBB, the corner of his mouth leaked a trace of gratifying laughter, and he talked with Low BBB for a day with a long focus.

The boss said:
Writing code should follow the principle of open and closed. Although this principle is object-oriented development, it is also applicable to functional programming. In short, it stipulates that the implemented functional code is not allowed to be modified, but can be extended, that is:

Closed: Implemented Functional Code Block
Openness: Extension Development
If the principle of open and closed is applied to the above requirements, then it is not allowed to modify the code inside the functions f1, f2, f3 and f4. The boss gives Low BBB an implementation scheme:

def w1(func):
    def inner():
        # Verification 1
        # Verification 2
        # Verification 3
        func()
    return inner

@w1
def f1():
    print('f1')
@w1
def f2():
    print('f2')
@w1
def f3():
    print('f3')
@w1
def f4():
    print('f4')

For the above code, only modifying the code of the underlying platform can realize the operation of [validation] before other people call the function f1 f2 f3 f4, and other business departments need not do any operation.

Low BBB asked in horror, what is the internal execution principle of this code?

The eldest brother was about to get angry. Suddenly, the mobile phone of Low BBB dropped to the ground. The screen saver happened to be the picture of his girlfriend. As soon as he looked at it, he shook and smiled, and decided to make a good friend with Low BBB.

Detailed explanations are given at the beginning:

Take f1 as an example:

def w1(func):
    def inner():
        # Verification 1
        # Verification 2
        # Verification 3
        func()
    return inner

@w1
def f1():
    print('f1')

The python interpreter interprets the code from top to bottom, as follows:

Def w1 (func): ==> load w1 function into memory
@w1
Yes, on the face of it, the interpreter will only interpret these two lines of code, because the function's internal code will not be executed until it is called.

On the face of it, the interpreter will actually execute these two sentences, but there is a big article in the @w1 code. The @function name is a kind of grammatical sugar of python.

The above example @w1 will perform an operation internally:
Execute w1 function
Execute the W1 function and take the function under @w1 as a parameter of the W1 function, that is, @w1 is equivalent to w1(f1), so it will be executed internally:

def inner(): 
    #Verification 1
    #Verification 2
    #Verification 3
    f1()    # Func is a parameter, where func equals f1 
return inner# Inner, inner is a function, not an executable function, which is actually to put the original f1 function into another function.

Return value of w1
The return value of W1 function is assigned to the function name F1 of the function under @w1, that is, the return value of W1 is reassigned to f1.

new f1 = def inner(): 
            #Verification 1
            #Verification 2
            #Verification 3
            //Original f1()
        return inner

So, when the business department wants to execute the f1 function, it will execute the new f1 function. First, it will execute the validation inside the new f1 function, then it will execute the original f1 function, and then return the return value of the original f1 function to the business caller.

In this way, the function of validation is executed, the content of the original f1 function is executed, and the return value of the original f1 function is returned to the business call.

Do you understand Low BBB? If you don't understand, I'll go to your house at night and help you solve it!!!

3. Re-discussion on Decorators

# Definition function: Complete package data
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

# Definition function: Complete package data
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
def test1():
    return "hello world-1"

@makeItalic
def test2():
    return "hello world-2"

@makeBold
@makeItalic
def test3():
    return "hello world-3"

print(test1())
print(test2())
print(test3())

Operation results:

<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>

4. Decorator function

Introducing logs
Function execution time statistics
Pre-processing before executing functions
Post-function cleanup
Scenarios such as privilege checking
cache

5. Examples of Decorators

Example 1: Functions without parameters

from time import ctime, sleep

def timefun(func):
    def wrapped_func():
        print("%s called at %s" % (func.__name__, ctime()))
        func()
    return wrapped_func

@timefun
def foo():
    print("I am foo")

foo()
sleep(2)
foo()

The above code understands the decorator's execution behavior as follows

foo = timefun(foo)
# After foo is assigned to func as a parameter, foo receives wrapped_func pointing to the return of timefun
foo()
# Call foo(), that is, wrapped_func()
# The internal function wrapped_func is referenced, so the func variable (free variable) of the external function is not released.
# The original foo function object is saved in func

Example 2: Decorated functions have parameters

from time import ctime, sleep

def timefun(func):
    def wrapped_func(a, b):
        print("%s called at %s" % (func.__name__, ctime()))
        print(a, b)
        func(a, b)
    return wrapped_func

@timefun
def foo(a, b):
    print(a+b)

foo(3,5)
sleep(2)
foo(2,4)

Example 3: Decorated functions have variable length parameters

from time import ctime, sleep

def timefun(func):
    def wrapped_func(*args, **kwargs):
        print("%s called at %s"%(func.__name__, ctime()))
        func(*args, **kwargs)
    return wrapped_func

@timefun
def foo(a, b, c):
    print(a+b+c)

foo(3,5,7)
sleep(2)
foo(2,4,9)

Example 4: return in Decorator

from time import ctime, sleep

def timefun(func):
    def wrapped_func():
        print("%s called at %s" % (func.__name__, ctime()))
        func()
    return wrapped_func

@timefun
def foo():
    print("I am foo")

@timefun
def getInfo():
    return '----hahah---'

foo()
sleep(2)
foo()


print(getInfo())

Implementation results:

foo called at Fri Nov  4 21:55:35 2016
I am foo
foo called at Fri Nov  4 21:55:37 2016
I am foo
getInfo called at Fri Nov  4 21:55:37 2016
None

If the decorator is modified to return func(), the result of operation is as follows:

foo called at Fri Nov  4 21:55:57 2016
I am foo
foo called at Fri Nov  4 21:55:59 2016
I am foo
getInfo called at Fri Nov  4 21:55:59 2016
----hahah---

Summary:
In general, in order to make the decorator more versatile, you can return
Example 5: Decorator with parameters, on the basis of the original decorator, set external variables

#decorator2.py

from time import ctime, sleep

def timefun_arg(pre="hello"):
    def timefun(func):
        def wrapped_func():
            print("%s called at %s %s" % (func.__name__, ctime(), pre))
            return func()
        return wrapped_func
    return timefun

# The following decoration process
# 1. Call timefun_arg("itcast")
# 2. Return the return value of step 1, time_fun, and then time_fun(foo)
# 3. Return the result of time_fun(foo), wrapped_func
# 4. Let foo = wrapped_fun, that is, foo now points to wrapped_func
@timefun_arg("itcast")
def foo():
    print("I am foo")

@timefun_arg("python")
def too():
    print("I am too")

foo()
sleep(2)
foo()

too()
sleep(2)
too()

It can be understood as

foo()==timefun_arg("itcast")(foo)()

Example 6: Class Decorator (Extension, Non-emphasis)
The decorator function is actually an interface constraint that accepts a callable object as a parameter and then returns a callable object. In Python, callable objects are functions, but there are exceptions. As long as an object overrides the call() method, the object is called able.

class Test():
    def __call__(self):
        print('call me!')

t = Test()
t()  # call me

Class Decorator demo

class Test(object):
    def __init__(self, func):
        print("---Initialization---")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---Functions in Decorators---")
        self.__func()
#Explain:
#1. When using Test as a decorator to decorate the test function, an instance object of Test is created first.
#   And pass the function name test as a parameter to the _init_ method
#   That is, the attribute _func in the _init_ method points to the function pointed to by the test.
#
#2. test points to instance objects created with Test
#
#3. When invoked using test(), it is equivalent to having the object (), so the _call_ method of the object is invoked.
#
#4. In order to call the function body pointed to by the original test in the _call_ method, an instance attribute is needed in the _init_ method to save the reference of the function body.
#   So we have the code self. _func = func, so that we can call the function body before test in the call _call_ method.
@Test
def test():
    print("----test---")
test()
showpy()#If you annotate this sentence and re-run the program, you will still see "--initialization--"

The results are as follows:

---Initialization---
func name is test
---Functions in Decorators---
----test---

Posted by harlowhair on Mon, 09 Sep 2019 06:48:00 -0700