Register to get access to free programming courses with interactive exercises

Closures Python: Functions

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 of inner and will not be remembered by the closure
  • However, the variable a is involved in generating the result of the inner 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.

About Hexlet learning process

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.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs
profession
new
Developing web applications with Django
10 months
from scratch
under development
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.