I’ve written two posts on functions in my blog, the first on Python functions being class I objects, the second on Lambda functions, and today on Python closures.

What is a closure? What are closures good for? Why use closures? Today we’ll walk through closures step by step with these three questions.

Closures are closely related to functions, and it is important to introduce closures with some background, such as nested functions and the scope of variables

scope

Scope refers to the scope within which variables can be accessed during program runtime. Variables defined in a function are local variables. The scope of a local variable can only be within the scope of the function, which cannot be referenced outside the function.

Variables defined in the outermost layer of a module are global variables, which are globally visible, but can also be read in functions. Such as:

num = 10 Global scope variables
def foo(a):
    print(num)  # 10Copy the code

Local variables cannot be accessed outside the function. Such as:

def foo(a):
    num = 10
print(num)  # NameError: name 'num' is not definedCopy the code

Nested function

Functions can be defined not only in the outermost layer of a module but also inside another function. Nested functions such as:

def print_msg(a):
    # print_msg is a peripheral function
    msg = "zen of python"

    def printer(a):
        # printer is a nested function
        print(msg)
    printer()
Output Zen of Python
print_msg()Copy the code

For nested functions, it can access non-local variables declared in its outer scope, such as the MSG variable in the code example that can be accessed by printer, a nested function.

Is it possible that local variables can be accessed outside the scope of the function itself? The answer is closures

What is a closure

As a first class object, a function can be returned as its return value. Now consider the following example:

def print_msg(a):
    # print_msg is a peripheral function
    msg = "zen of python"
    def printer(a):
        # printer is a nested function
        print(msg)
    return printer

another = print_msg()
Output Zen of Python
another()Copy the code

This code has exactly the same effect as the previous example, again printing “Zen of Python”. The difference is that printer, the internal function, is returned directly as the return value.

Normally, local variables in a function are only available during the execution of the function, and once print_msg() is executed, we assume that the MSG variable is no longer available. However, here we see that after print_msg is executed, the MSG variable is printed normally when another is called. This is what closures do. Closures make it possible for local variables to be accessed outside the function.

After looking at this example, let’s define the closure again. Wikipedia says:

In computer science, a Closure, short for Lexical Closure, is a function that references a free variable. The referenced free variable will exist with the function, even if it has left the environment in which it was created. Therefore, there is another way to say that closures are entities composed of functions and their associated reference environments.

A closure is essentially a function that has two parts: printer function and MSG. Closures keep the values of these variables in memory at all times.

A closure, as its name suggests, is a closed package that contains free variables, just like property values defined in a class. The visibility of the free variables goes with the package, and the free variables are accessible wherever the package is accessible.

Why use closures

Closures avoid the use of global variables. In addition, closures allow functions to be associated with some data (the environment) on which they operate. This is very similar to object-oriented programming, where objects allow us to associate certain data (properties of objects) with one or more methods.

In general, closures are a better choice when there is only one method in the object. Here’s an example:

def adder(x):
    def wrapper(y):
        return x + y
    return wrapper

adder5 = adder(5)
Output # 15
adder5(10)
Output # 11
adder5(6)Copy the code

This is more elegant than using classes, and decorators are one of the applications based on closures.

All functions have a __closure__ attribute, which returns a tuple of cell objects if the function is a closure. The cell_contents property of the cell object is the free variable in the closure.

>>> adder.__closure__
>>> adder5.__closure__
(<cell at 0x103075910: int object at 0x7fd251604518>,)
>>> adder5.__closure__[0].cell_contents
5Copy the code

This explains why a local variable can be accessed outside of the function after it leaves it, because it is stored in the closure’s cell_contents.

Blog: foofish.net/python-clos… Public account: Python Zen (ID :VTtalk), share Python and other technical dry goods

Zen Python