There have been numerous articles on Python closures on the web, but this article will get to know closures by addressing a requirement problem.

The requirement is this: we need to keep track of our study time, in minutes. So if I study for 2 minutes, I return 2, and then after a while, I study for 10 minutes, I return 12, and so on and so on.

To do this, we create a global variable to record the time, and then use a method to add each learning time, usually in the following form:

time = 0

def insert_time(min):
    time = time + min
    return  time

print(insert_time(2))
print(insert_time(10))
Copy the code

Think about it seriously. Is there a problem?

Actually, this is an error in Python. The following error is reported:

UnboundLocalError: local variable 'time' referenced before assignment
Copy the code

That’s because, in Python, if a function uses the same name as a global variable and changes its value, the variable becomes local, and we reference it without defining it in the function, so we get this error.

What if you do want to reference a global variable and modify it in a function?

We can use the global keyword, modified as follows:

time = 0


def insert_time(min):
    global  time
    time = time + min
    return  time

print(insert_time(2))
print(insert_time(10))
Copy the code

The following output is displayed:

2 to 12Copy the code

However, global variables are used, and we should avoid using global variables if we can in our development. Because different modules and 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 modifies time at the same time. If there is an error in time, it is difficult to find and correct the error.

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

Is there a better way?

Let’s use closures to solve this 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)
Copy the code

The following output is displayed:

2 0 0 12Copy the code

The most direct manifestation of this is that the global variable time has not been modified at all, and the nonlocal keyword is used to indicate the use of outer (non-global) variables in functions or other scopes. So what is the specific operation process of the above code? We can take a look at the following image:

This local scope of an inner function can access the behavior of variables in the local scope of an outer function, known as: closures. More directly, when a function is returned as an object, it creates a closure with external variables. k

Closures avoid the use of global variables. In addition, closures allow functions to be associated with some data (the environment) on which they operate. And with closures, you can make your code more elegant. And the decorators in the next article are also based on closures.

At this point, there’s a problem, you call it a closure because it’s a closure, right? Is there any way to verify that this function is a closure?

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

Let’s print it out and try it out:

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)
Copy the code

The printed result is:

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

As you can see from the print, the value passed in is always stored in the closure’s cell_contents, so this is the best feature of a closure: you can bind the variables of the parent function to the function defined inside. The closure exists even if the parent function that generated it has been released.

Closure is actually the process of the class (the father) generated instances (closures), only different is the parent function in the call when the execution, execution will be released after the completion of the environment, while the class files created during execution, general scope only released after the program execution, and so on some need to reuse the function enough to be defined as the behavior of the class, Using closures takes fewer resources than using classes, and is lighter and more flexible.

Welcome to scan wechat and follow the wechat official account: