Closure

There are many articles introducing Python closures on the Internet. This article will learn about closures by solving a requirement problem.

This requirement is like this, we need to keep recording our study time in minutes. For example, if I study for 2 minutes, I will return 2. Then after a while, if I study for 10 minutes, I will return 12. The learning time is accumulated like this.

Faced with this demand, we usually create a global variable to record the time, and then use a method to add each learning time, usually written in the following form:

time = 0
def insert_time(min):
    time = time + min
    return  time
print(insert_time(2))
print(insert_time(10))

Seriously Think about it, is there any problem?

Actually, this will report an error in Python. The following error will be reported:

UnboundLocalError: local variable 'time' referenced before assignment

That is because, in Python, if a function uses the same name as a global variable and changes the value of the variable, then the variable will become a local variable, then This will cause us to reference it in the function without defining it, so this error will be reported.

If you really want to reference a global variable and modify it in a function, what should you do?

We can use the global keyword, and the specific modifications are as follows:

time = 0
def insert_time(min):
    global  time
    time = time + min
    return  time
print(insert_time(2))
print(insert_time(10))

The output results are as follows:

2
12

But, global variables are used here, we can try our best during development Avoid using global variables as much as possible. Because different modules and different functions can freely access global variables, global variables may be unpredictable. For example, programmer A modifies the value of the global variable time, and then programmer B also modifies time. If there are errors, such errors are difficult to find and correct.

Global variables reduce the versatility between functions or modules. Different functions or modules depend on global variables. Similarly, global variables reduce the readability of code, and readers may not know that a certain variable being called is a global variable.

Is there a better way?

At this time, we use closures to solve the problem. Let’s look at the code directly:

time = 0
def study_time(time):
    def insert_time(min):
        nonlocal  time
        time = time + min
        return time
    return insert_time
f = study_time(time)
print(f(2))
print(time)
print(f(10))
print(time)

The output results are as follows:

2
0
12
0

The most direct manifestation here is the global variable time. So far. It has not been modified in the end. The nonlocal keyword is still used here, indicating the use of outer (non-global) variables in functions or other scopes. So what is the specific running process of the above code. We can look at the picture below:

a4e16c80e1df62cb932f66f1b8b91c9.png

This kind of behavior in which the variables in the local scope of the external function can be accessed in the local scope of the internal function is called: closure. A more direct way of expressing it is that when a function is returned as an object, external variables are included, forming a closure. k

Closures avoid the use of global variables. In addition, closures allow a function to be associated with some data (environment) it operates on. And using closures can make the code more elegant. And the decorators mentioned in the next article are also implemented based on closures.

At this point, there will be a question. Do you think it is a closure or a closure? Is there any way to verify that this function is a closure?

Yes, all functions have a __closure__ attribute. If the function is a closure, then it returns a tuple object composed of cells. The cell_contents property of the cell object is the variable stored in the closure.

Let’s print it out and experience it:

time = 0
def study_time(time):
    def insert_time(min):
        nonlocal  time
        time = time + min
        return time
    return insert_time
f = study_time(time)
print(f.__closure__)
print(f(2))
print(time)
print(f.__closure__[0].cell_contents)
print(f(10))
print(time)
print(f.__closure__[0].cell_contents)

The printed result is:

(<cell at 0x0000000000410C48: int object at 0x000000001D6AB420>,)
2
0
2
12
0
12

It can be seen from the printed result that the passed in value is always stored in the cell_contents of the closure, so , this is the biggest feature of closure, it can bind the variables of the parent function to its internally defined function. Even if the parent function that generated the closure has been released, the closure still exists.

The process of closure is actually like a class (parent function) generating an instance (closure). The difference is that the parent function is only executed when called. After execution, its environment will be released, while the class is executed in the file. It is created at the time, and the scope is generally released after the program is executed. Therefore, for some functions that need to be reused and cannot be defined as a class, using closures will occupy fewer resources than using classes, and is more lightweight and flexible.

Continuing Learning
||
submitReset Code