Do you remember the inner
function we created in the first lesson on FVP? So, this function was a closure.
A closure function is a function that accesses local variables and uses them in its body in the scope we created it in. It makes closures different from regular functions, which can use only their arguments and global variables.
Let us look at an example demonstrating a closure, and we will use it to show you what it is and how it works:
G = 10
def make_closure():
a = 1
b = 2
def inner(x):
return x + G + a
return inner
In this example, inner
is a closure:
- The
b
variable is not in the body ofinner
and will not be remembered by the closure - However, the variable
a
is involved in generating the result of theinner
call, and therefore we remember its value - The variable
G
is global if we accept the fact that the specified code is at the top module level and is not nested in any other blocks
By the way, functions created at the top module level, not inside other functions, are not closures. They do not lose anything because all external names visible to these functions are global. It works for imports and definitions from this module located higher in the code.
Memorizing the variable values
We need to mention an important feature: the actual memorization of the value occurs when the scope that the closure was created in ends, as it were, for example, when the current function has finished executing.
The easiest way to think of it is to imagine that when we create the function, we describe the closed variables like they should not forget to remember the current value of the variable xyz
. These items execute the body of the function that created the closure has finished.
Here is an example demonstrating this latest memorization:
def make_closure():
y = 1
def inner(x):
return x + y
y = 42
return inner
make_closure()(100)
# 142
Here inner
gets 42
as the stored value, even though the assignment of this value to the y
variable occurs after the function declaration. Closing a loop variable looks even funnier:
printers = []
for i in range(10):
def printer():
print(i)
printers.append(printer)
printers[0]()
# 9
printers[5]()
# 9
printers[9]()
# 9
i = 42
printers[0]()
# 42
It would seem that we created a dozen functions, each of which should output its number, but all of them print the last value of the loop variable.
Like before, memorization occurs when we exit the scope where we define the variable. But, this scope had not finished when the closures were called (in this REPL example, it will end only when exitingREPL). Therefore, when exiting the loop, all closures output 9
, and when we change the value of i
, the output value also changes.
Fighting short circuits
How do you remember what you need and when you need it? How do we fix the loop example so that each function prints its value and does not react to further changes to the loop variable? We need to close the variable in the scope, which will immediately end after we create the closure.
We can achieve it by wrapping the creation of the function in another function. Here is the code:
printers = []
for i in range(10):
def make_printer(arg):
def printer():
print(arg)
return printer
p = make_printer(i)
printers.append(p)
printers[0]()
# 0
printers[5]()
# 5
The result is positive. But how does this code work?
Note that this time, printer
closes the value of the arg
variable, and it belongs to the make_printer
function and is visible only while we execute the function body.
The returned closure still gets its value when we exit from the body of make_printer
. And since we call the make_printer
function with different arguments, the closures get different values.
This technique of wrapping it in a function and immediately calling it is not a crutch exclusive to Python. Many other languages with a closure mechanism also use it. For example, JavaScript programmers do the same thing.
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.