18. Advanced functions (closures and decorators)

1. Closure function

1. Closure basics

We have learned about function nesting before, let's look at an example about function nesting.

The outer function outer_1 defines an empty list lst, and then calls the inner function inner_1, passing a parameter 1 to the inner function each time it is called. When inner_1 is executed, a 1 is added to lst, and then lst is passed through the outer function Returned to the calling function.

def outer_1():
    lst = []
    def inner_1(num1):
        lst.append(num1)
    inner_1(1)
    return lst
f1 = outer_1()
f2 = outer_1()
print(f1)
print(f2)

# 输出:
[1]
[1]

We found that the value of f1 is [1], and the value of f2 is also [1]. The value of lst will be initialized to an empty list every time it is run. Is there a way for lst to keep saving the elements stored in it?

Local variables will be automatically released in memory after the function is executed. If we want local variables to last for a long time, we can use closure functions.

Next, look at the structure of the closure function.

def outer_1():                 # 动作2 进入outer_1执行过程
    lst = []                   # 动作3 生成一个空列表 
    print("I am outer_1")
    def inner_1(num1):         # 动作4 定义一个名为inner_1的内层函数 # 动作8 将参数1传给num1 # 动作13 将参数1传给num1
        lst.append(num1)       # 动作9 向lst添加元素1   # 动作14 向lst再添加元素1
        return lst             # 动作10 将lst返回       # 动作15 将lst返回
    return inner_1             # 动作5 将函数inner_1返回,此时f1获得inner_1函数
f1 = outer_1()                 # 动作1 调用函数outer_1  # 动作6 将返回的结果赋值给f1
print(f1(1))                   # 动作7 调用函数f1(1)    # 动作11 将返回的lst打印出来
print(f1(1))                   # 动作12 再次函数f1(1)   # 动作16 将返回的lst打印出来

# 输出
I am outer_1
[1]
[1, 1]

print(type(f1)) # 输出 <class 'function'>
print(f1) # 输出 <function outer_1.<locals>.inner_1 at 0x109aeb550>

The outer function defines an empty list lst, the inner function refers to the lst variable of the outer function, uses append to add elements, and returns lst, and the last line of the outer function returns the inner function name as an object.

Let's take a closer look at how the program is executed:

Action 1: f1=outer_1(), execute the function on the right side of the equal sign first

Action 2: Enter the execution process of outer_1

Action 3: Produce an empty list named lst

Action 4: Define an inner function named inner_1

Action 5: Return the function inner_1

Action 6: Assign the returned result to f1, at this time f1 has obtained the inner function inner_1

Action 7: Call the function f1(1), passing a parameter to inner_1()

Action 8: Enter the internal function directly, and the formal parameter num receives the value 1

Action 9: Add element 1 to lst

Action 10: return lst

Action 11: Print the returned lst

We found that the outer function is only executed during the assignment of f1=outer_1(), and then f1(1) is just a call to the inner function inner_1. The process of executing f1(1) for the second time is basically similar, except that during the second execution, lst is not deleted from the memory, and the result of the first execution is still retained, so after the second execution, lst The value is [1,1].

If you feel a little dizzy to understand this process, another good way is to debug the above code line by line and observe the execution process, which is much easier to understand.

2. Closure formation conditions

If a function is nested inside a function, and the inner function refers to variables in the outer scope (non-global scope), then the inner function is called a closure. Closures remember the values ​​of variables in the outer scope they refer to each time they are run.

Closure formation conditions:

(1) A function nests another function;

(2) The inner function references the variables of the outer function;

(3) The outer function returns the inner function as a return value.

3. Free variables

In the above example, inner_1 refers to the variable lst of the outer function outer_1. Once the outer function outer_1 is executed, inner_1 becomes a closure function, and the variable lst referenced by the inner function is called a free variable.

The free variable will form a binding relationship with the inner function, and is associated with a specific instance of the closure. The free variables referenced by each instance of the closure do not interfere with each other. Let's understand this mutual non-interference through an example .

def outer():
    lst = []
    def inner(num):
        lst.append(num)
        print('lst:', lst)
    return inner
f1 = outer()
f2 = outer()

f2(1)
f1(2)
f2(2)

输出:
lst: [1]
lst: [2]
lst: [1, 2]

In this code, through f1 = outer(), f2 = outer(), we generate two closure instances, they are f1, f2 respectively.

When f2(1) is executed, the closure function is executed, and a parameter 1 is passed to inner, lst is empty at this time, and an element 1 is added to it, so the result of lst at this time is [1].

When f1(1) is executed, the closure function is executed, and a parameter 1 is passed to inner, lst is still empty at this time, and an element 1 is added to it, so the result of lst at this time is [1].

When f2(2) is executed, the closure function is executed and a parameter 2 is passed to inner, because f2(1) has been executed, lst is already [1], and an element 2 is added to it, so the result of lst at this time is [1,2].

Through the above process, we can verify and conclude that lst exists in both f1 and f2, and they do not interfere with each other.

In addition to being defined in the outer function, free variables can also be used as virtual parameters and passed in through the outer function. Please see the example below.

def outer(num1):
    def inner(num2):
        result = num1 + num2
        print('result:', result)

    return inner

f1 = outer(1)
f1(2)

输出:
result: 3

At this time, the outer function has a virtual parameter. When generating a closure instance, a parameter needs to be passed to the outer function. Here, the value of the passed parameter is 1, which is stored in num1 after being received by the outer function. When the function f1(2) is executed, it is equivalent to calling the inner function inner, because inner also has a parameter, so a parameter is also passed in when calling, and the parameter value is 2, which is saved after being received by the inner function in num2. When the inner function is executed, num1 refers to the num1 of the outer function, its value is 1, the value of num2 is 2, and the result of the addition is 3. The final output result: 3.

2. Decorator

1. Decorator Basics

The essence of a decorator is the use of closures. Its function is to add functions without changing the original function and function name.

Principles: 1. The source code of the decorated function cannot be modified; 2. The calling method of the decorated function cannot be modified.

Decorator application scenarios:

  • import log
  • Function execution time statistics
  • Preparing before executing the function
  • Cleanup function after function execution
  • Scenarios such as permission verification
  • cache

Example: We have the following function. By adding a decorator, we can add a dividing line above and below the original output content.

The basic functions are as follows:

def f():
    print('---test---')
f()

We use closures to accomplish this task.

def wrapper(func):
    def inner():
        print("*****我是美丽分割线1*****")
        func()
        print("*****我是美丽分割线2*****")
    return inner
def f():
    print('---test---')
f = wrapper(f)
f()

Let's see how to use decorators:

def wrapper(func):
    print('正在装饰')
    def inner():
        print("*****我是美丽分割线1*****")
        func()
        print("*****我是美丽分割线2*****")
    return inner

@wrapper
def f():
    print('---test---')
f()

Let's look at its execution process:

(1) def wrapper(func), define the decorator function and load it into memory.

(2) def f(), define the function. At this time, it is found that there is @wrapper above it. @wrapper calls the decorator function. @wrapper passes f as a parameter to the wrapper. This sentence is equivalent to f= wrapper(f).

(3) At this time wrapper(func) will be executed immediately, output "decorating", and then inner will be returned to f, at this time f = inner()

(4) f() is an execution function, and inner() is actually executed, so it will output "*****I am a beautiful dividing line 1*****", func() is for f() ontology Call, at this time, it will output '---test---', and finally output "*****I am a beautiful dividing line 2*****".

We found that @wrapper is a simplification of f=wrapper(f).

In actual use, a decorator can decorate multiple functions, just add a sentence above each decorated function to call the decorator function.

import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s'%(stop_time-start_time))
    return deco

@timer      #@timer 相当于就是test1 = timer(test1);此时test1=deco,deco里面的func=最开始的test1
def test1():
    time.sleep(3)
    print('in the test1')
@timer      #@timer 相当于就是test2 = timer(test2);此时test2=deco,deco里面的func=最开始的test2
def test2():
    time.sleep(5)
    print('in the test2')

test1()
test2()

2. Multiple decorators

Multiple decorators can be applied to a function, the order of decoration is bottom-up, and the order of invocation is top-down.

def wrapper_out1(func):
    print('wrapper_out1正在装饰')
    def inner1():
        print("这里是inner1-1")
        func()
        print("这里是inner1-2")
    return inner1

def wrapper_out2(func):
    print('wrapper_out2正在装饰')
    def inner2():
        print("这里是inner2-1")
        func()
        print("这里是inner2-2")
    return inner2
@wrapper_out1
@wrapper_out2
def test():
    print("--test--")

test()

# 输出
wrapper_out2正在装饰
wrapper_out1正在装饰
这里是inner1-1
这里是inner2-1
--test--
这里是inner2-2
这里是inner1-2

Implementation process:

(1) Load wrapper_out1, wrapper_out2, test into memory

(2) Decorate the test first, because there are two decorators on the test, and the decoration follows the bottom-up principle, first execute @wrapper_out2, which is equivalent to test = wrapper_out2(test), output 'wrapper_out1 is decorating', and then inner2 is passed back to test; when @wrapper_out1 is executed, it is equivalent to test = wrapper_out1(test), note that the test in the brackets is actually inner2, output 'wrapper_out1 is being decorated', and then inner1 is passed back to test. This whole process is equivalent to: test = wrapper_out1(wrapper_out2(test)), the test in brackets is the original test.

(3) When calling the test function, execute inner1 first, output: "here is inner1-1", and then call func(), where func is actually inner2, so enter inner2, output "here is inner2-1", in inner2 The func is the original test function, so output "--test--", and then output "here is inner2-2", after inner2 is executed, return to inner1, and output "here is inner1-2".

3. Decorate functions with parameters and return values

def func_dec(func):
    def wrapper(*args):
        print("average_value is %.2f."%(sum(args)/len(args)))
        result = func(*args)
        return result
    return wrapper

@func_dec
def add_sum(*args):
    return sum(args)

args = range(10)
result = add_sum(*args)
print(result)

# 输出
average_value is 4.50.
45

3. Advanced function exercises

1. Decorator basic practice

Make a decorator to add a string above and below the string of the existing function, the effect is as follows:

def test():
    print("这是第一次装饰器编程练习")
test()

# 输出
Hello world
这是第一次装饰器编程练习
I love python

2. Use the timer to decorate the function

Make a decorator to output the running time of each function based on the existing functions.

def test1():
    time.sleep(5)
    print("这是第一次装饰器编程练习")

def test2():
    time.sleep(8)
    print("这是第二次装饰器编程练习")
test()

3. Write a decorator with parameters

def test(*args):
    print("这是第一次装饰器编程练习")
test('20010001','Tom')

# 输出
Id is 20010001,Name is Tom
这是第一次装饰器编程练习

4. Answers to Lesson 16 Dictionary Exercises

1. Basic dictionary exercises

dic = {
    'python': 95,
    'html/css': 99,
    'c': 100
}

'''
1.字典的长度是多少
2.请修改'html/css'这个key对应的value值为98
3.删除c这个key
4.增加一个key - value对,key值为php, value是90
5.获取所有的key值,存储在列表里
6.获取所有的value值,存储在列表里
7.判断javascript是否在字典中
8.获得字典里所有value的和
9.获取字典里最大的value
10.获取字典里最小的value
11.字典dic1 = {'php': 97}, 将dic1的数据更新到dic中
'''

print(len(dic))
dic['html/css'] = 98
del dic['c']
dic['php'] = 90
lst_key = list(dic.keys())
lst_value = list(dic.values())
print('javascript' in dic)
print(sum(dic.values()))
print(max(dic.values()))
print(min(dic.values()))
dic1 = {'php': 97}
dic.update(dic1)

2. Find the b-value

There are 17 key-value pairs in the dictionary D, and each key-value pair is stored in the form of (a,b):v. It is required to write a program to find the value of b when the conditions a==10 and v==1 are satisfied.

D = {(4, 7): 0, (2, 6): 1, (10, 4): 1, (5, 11): 1, (4, 5): 1,
(2, 8): 0, (8, 11): 0, (10, 0): 1, (6, 11): 1, (9, 11): 1,
(1, 9): 0, (10, 1): 0, (7, 11): 1, (0, 9): 1, (3, 7): 1,
(10, 3): 1, (10, 2): 1}

for a,v in D.items():
    if a[0] == 10 and v == 1:
        print(a[1])

3. Word frequency statistics

# 任务1:将所有大写转换为小写
words = words.lower()

# 任务2:生成单词列表
words_list = words.split()

# 任务3:生成词频统计
words_dict = {}
for word in words_list:
    if word in words_dict.keys():
        words_dict[word] += 1
    else:
        words_dict[word] = 1

# 任务4:排序
words_dict_list = list(words_dict.items())
words_dict_list.sort(key = lambda x:x[1],reverse=True)
print(words_dict_list)
# 任务5:排除语法型词汇,代词、冠词、连词
exclude = ["the", "has", "of", "a", "for", "and","away","from","on","to","are","only","have","in","after"]

for i in range(len(words_dict_list)-1,-1,-1):
    k = words_dict_list[i][0]
    if k in exclude:
        words_dict_list.pop(i)

# 任务6:输出词频最大TOP20
print(words_dict_list[:20])

4. Search for ID number

The ID number contains a lot of information, the 7th to 14th digits are birthday information, the penultimate digit is the gender code, odd numbers for men and even numbers for women. It is required to output the three elements in the account list in the format of the info dictionary.

account = ['Jone360403200105070312', 'Tom36040320020903043X', 'Susan360101200108110181']
info = {}
for x in account:
    name = x[:len(x) - 18]
    year = x[-12:-8]
    month = str(int(x[-8:-6]))
    day = str(int(x[-6:-4]))
    sex = lambda x:'女' if int(x[-2]) % 2 == 0 else '男'
    info[name] = dict(性别=sex(x), 生日='%s年%s月%s日' % (year, month, day))
print(info)

 

Guess you like

Origin blog.csdn.net/qq_40407729/article/details/111876295