SoFunction
Updated on 2024-11-16

Explanation of Variable Scoping Problems of Closures in Python Programming

closure (math)

When we use return functions, since we need to return to another function in one function, we need to redefine a function in that function. This creates the problem of nested functions. The function outside is an outer function relative to the function inside (outer function), and the one inside we call his inner function (inner function).

def outerFunction(): #external function
    def innerFunction(): #Internal functions
        x = 1
        return x
    return innerFunction # The return value is a function
a = outerFunction()
print(a)

When we print the value of a here, we are actually printing the address of our return function:

<function outerFunction.<locals>.innerFunction at 0x0000019C278C0E50>

Typically, when we use a function as a return value, we use variables from our outer function in the inner function, and this situation creates an interesting thing. The inner function will be returned to the other caller outside, and our variable is defined in our outer function. At this point, what happens to the scope of our variable?

Testing:

def outerFunction(x): #external function
    y = 10
    def innerFunction(): #Internal functions
        return x + y
    return innerFunction # The return value is a function
a = outerFunction(10)
print(a())

Print:

20

Here you can see that when we assigned a value to a, we passed in a value of 10 to the outer function at the same time, and then, we printed out a() directly, and at this point, our a returned a value of 20, indicating that our variable and its arguments are retained by our inner function after it enters the inner function.

Here, we call this phenomenon "Closure", I try to return to the inner function several times to see if they have the same address:

def outerFunction(x): #external function
    y = 10
    def innerFunction(): #Internal functions
        return x + y
    return innerFunction # The return value is a function
a = outerFunction(10)
b = outerFunction(20)
c = outerFunction(30)
print(a())
print(b())
print(c())
print(a)
print(b)
print(c)

Print:

20
30
40
<function outerFunction.<locals>.innerFunction at 0x0000020C480C0DC0>
<function outerFunction.<locals>.innerFunction at 0x0000020C480C0D30>
<function outerFunction.<locals>.innerFunction at 0x0000020C480CD280>

Here we can see that each innerFunction has a different address, and when we call the return function multiple times, each time we call it, our return function creates a new variable given to us by the function.

So, if we call our inner function multiple times inside our outer function, what does the scope of our outer function's variables look like?

Let's test it:

def outerFunction(x): #external function
    L=[] # Define a List
    y = 10
    for i in range(1, 4):
        def innerFunction(): #Internal functions
            return (x + y) * i # Use variables from our outer functions
        print(innerFunction()) # Print the value returned by the inner function
        #print(innerFunction)
        (innerFunction) # Add the inner function to our list.
    return L # Return this List
a = outerFunction(10)
print(a[0])
print(a[1])
print(a[2])
print(a[0]())
print(a[1]())
print(a[2]())

See print:

20
40
60
<function outerFunction.<locals>.innerFunction at 0x00000274AD6B0E50>
<function outerFunction.<locals>.innerFunction at 0x00000274AD6BD040>
<function outerFunction.<locals>.innerFunction at 0x00000274AD6BD3A0>
60
60
60

Here we can see an interesting phenomenon:

When we call our inner function several times inside our function and print out its return value, we can see that the value is normal (in increasing order). If we pass the inner function into our List and call it from outside the function, the return values of our function all become 60, meaning that they only get the final value of the variable in our outer function. This is a phenomenon unique to our closures.

Variables in closures

Normally, when a function ends, in memory, the local variables inside our function are freed along with the function.

However, when we have a function that contains a closure, that is, when the return value of the function is a reference to a function. In this case, when the function ends, it passes the value of the local variable inside the function it's returning to, because the local variable inside it needs to be freed. Because of this, when we call our return function from the outside, we see that it uses the final value of the variable inside the function, because it was passed to the return function at the end of the function. At this point, the variable inside our function can only have one value.

However, there is another approach we can use to circumvent this:

def outerFunction(x): #external function
    L=[] # Define a List
    y = 10
    def innerFunction(i): #Internal functions
        def f():
            return (x + y) * i # Use variables from our outer functions
        return f
    for i in range(1, 4):
        print(innerFunction(i)()) # Print the return value of an inner function within an inner function
        (innerFunction(i)) # Add the inner function to our List.
    return L # Return this List
a = outerFunction(10)
print(a[0])
print(a[1])
print(a[2])
print(a[0]())
print(a[1]())
print(a[2]())

Print the following:

20
40
60
<function outerFunction.<locals>.innerFunction.<locals>.f at 0x00000155B0F80040>
<function outerFunction.<locals>.innerFunction.<locals>.f at 0x00000155B0F803A0>
<function outerFunction.<locals>.innerFunction.<locals>.f at 0x00000155B0F80430>
20
40
60

Here we can see that the return value of the function is the same when we call it externally as it is when we call it internally. So let's analyze the execution of the function and the scoping of the variables within the function.

First, we add a layer of inner function f() on top of our inner function, and, inside this inner function, we use the argument of the external inner function innerFunction as the return value. Then, inside the for loop, we add the return value of our innerFunction to our List.

Here's what we should have realized. Inside the for loop, when we add the return value f of our innerFunction to our List, since innerFunction belongs to the inner function of innerFunction as opposed to the function f(), when we return to the function f(), the value of f() is its final value, and at this point, our innerFunction function is still inside the for loop. When we return to the f() function, the value used inside the f() function is its final value, and at that point, our innerFunction function is still inside the for loop, so inside the loop, every time we call the return value of our innerFunction, the value we pass to our f() function is our final value.

At this point, even if we call this f() function externally, it returns the same value as if it were called internally.

Above is the detailed content of Python programming in the closure of the variable scope of the problem analysis, more information about Python closure of the variable scope of the information please pay attention to my other related articles!