Loading video player…

Creating a Closure

00:00 In the previous lesson, you defines outer function, print with memory(), and within it you defined the list record and then inner function print_().

00:09 print_() is the function you want to use instead of the built-in print() with a difference that this new function also keeps track of all the arguments you pass to it.

00:20 However, as we’ve seen, the scope doesn’t work yet. Why? Because print_() does not exist in the global scope. It’s a local variable to print_with_memory().

00:31 Let’s make a few changes. First of all, rather than calling this print_, let’s refactor it and rename it inner(). This makes it clear it’s the inner function, and it’ll be consistent with some other examples we’ll see later in this course.

00:46 Still inner only exists as a local variable within print_with_memory().

00:52 So what we can do is return it from print_with_memory(). We return the function name inner(). Notice that you’re not calling inner, you’re simply returning the name inner, which refers to the function.

01:07 Another thing to note is that this is a return statement belonging to the outer function print_with_memory(). It’s not the return statement of the inner function.

01:17 Since print_with_memory() returns a function, you can now define print_ in the global scope, and this is going to be equal to whatever print_ with_memory() returns.

01:30 Since print_with_memory() returns, the function inner, print_ is the same as the inner function. Before running this code, let’s comment out the final print() call.

01:42 Why? Because record is still a local variable to print_with_memory(). So we know it won’t work yet. We’ll deal with this in a bit.

01:51 So let’s run the script. And the two calls to print_ print out the two strings: "Real Python" and "I love Python".

02:03 However, you still cannot access the information that’s stored within the list record. And this is the whole point of recreating a new version of the print() function.

02:13 So let’s look at a few options.

02:17 One option is to print out the list every time you call the inner function. And therefore every time you call print_ in the global scope.

02:27 And this works. The first time you call print_, the list contains the string "Real Python", but the second time you call it the list now contains the strings "Real Python" and "I love Python".

02:39 However, you’re only displaying the list. You’re not getting access to the list within your code, so this is not an ideal solution either.

02:49 So let’s comment out this print() call. We don’t need it anymore. The second solution is not one that you will be using in code, but it’ll help you understand how closures work.

03:03 Let’s start by printing print_. So notice I’m using the built-in print() and its argument is the name print_. Let’s see what print_ refers to.

03:16 And you note that print_ is the function inner, which is one of the local variables of the function print_with_memory().

03:31 Now here’s where you really understand what the closure is. Whenever you have an inner function, which refers to a variable that was defined in the enclosing scope, the inner function will maintain a reference to that variable.

03:45 In this case, the inner function is the one you called inner. The variable is record, which is referenced from within inner, but it was defined in the enclosing scope, which is the local scope of the print_with_memory().

03:59 And in this case, the function inner is a closure because it’ll contain a reference to the list record, even when the enclosing function scope print_ with_memory() no longer exists.

04:12 Note how you’re only calling print_with_memory() once and assigning its return value to print_, however, print_ is now the closure, which will keep a reference to the list.

04:27 Let’s see how this is implemented in CPython. You can look at the attribute .__closure__, which is an attribute of the inner function.

04:37 So let’s run the script to see what closure looks like. And this attribute contains a tuple, which contains a cell object. And within it, this tells us this is a list to make sense of this let’s dig a bit deeper.

04:52 This tuple contains one element, so we can fetch the first element of .__closure__ and we’re going to look at its cell contents, and this will show us what it contains.

05:05 Let’s run the script again. And in the final line, you can see that the cell contents of the closure is the list that contains the strings "Real Python" and "I love Python".

05:16 Therefore, the print() function, which is a closure, contains within it a reference to the list which you originally referred to as record.

05:27 But in this case, this list is referenced by the closure. Every time you call print_, each call of print_ can communicate with previous calls and later calls through this list.

05:41 And that’s why every, in this case, every time you call print_, you are adding its argument to all the previous arguments that you pass to previous calls of print_.

05:53 This can feel quite confusing, so let’s review what we’ve done so far. So when an inner function refers to an object defined in its enclosing scope, the inner function maintains a reference to this object even when the enclosing function scope no longer exists.

06:10 Let’s see what this means in this example that we’re working on in this lesson. The inner function inner has a reference to the list that stores its arguments, even after the program completes the call to print_with_memory(), which is the outer function.

06:25 So the list is included within the closure.

06:29 Therefore, each time you call print_ this function, which is the closure, has access to the list even though the list was originally defined as a local variable in print_with_memory().

06:44 And this is the main idea behind a closure. However, using the .__closure__ attribute is not the way you want to access the data within the closure.

06:55 In the next lesson, we’ll look at another example and a different way of accessing this data.

Become a Member to join the conversation.