Accessing Variables in the Enclosing Scope
00:00 In this lesson, you look at another example. You’ll note many similarities between this example and one from the previous lessons. And that’s a good thing because you can use it to review the key concepts about closures.
00:11
However, there will be some important differences as well, and I’ll guide you through them. You’re still going to use the print()
built-in function, but this time you would like to keep track of how many times you call it within a program.
00:26
As you’ve done previously, you’ll create another function. Let’s call this print_with_counter()
.
00:33
Instead of a list to store arguments, all you need now is a variable counter
, which will count how many times we’ve called the function, so we’ll start with zero.
00:43
You still need to have your inner function and this will be, we’ll put a placeholder for now. This is the function that will replace your print()
, but this one will keep track of how many times it’s been called.
00:56
In the previous example, you put a single parameter here. However, you can make this more general by using *args
and **kwargs
. This allows this function to accept any number of positional arguments and any number of keyword arguments.
01:13
And in there, it needs to call print()
and you can pass args
and kwargs
back into print()
. So the inner function will print things out.
01:23
However, it needs to do something else as well. It needs to keep track of the counter, so you will ideally like to take your counter
and increment it by one.
01:33 You’ll see that there’s a problem with this line, but we’ll get to this soon.
01:38
Next, you need to return the inner function from within the outer function called print_with_counter()
.
01:46
And in the main code, you can now create print_
your version of print()
. And print_
be equal to whatever print_with_counter()
returns, and print_with_counter()
returns the inner function.
02:01
So now you have a function called print_
. You can call it a few times, Real Python
as the first argument, and the second time, as we’ve done earlier, you can call it with I love Python
or anything you want really.
02:17 So let’s try to run this code.
02:21
And this is where we get the problem. Python raises an error. We have an UnboundLocalError
. The issue is with the variable counter
within the function inner()
.
02:35
In the previous example, we did not have this problem and we said that the function inner()
has access to any variables defined within an outer function.
02:45
However, this line, let’s write it in full to make it clear. This line is equivalent to saying counter
is equal to counter
plus one, and therefore the inner()
function is creating its own local variable called counter
.
02:59
It’s no longer using the counter
used in the outer function. And therefore this line causes a problem because you’re defining a new local variable counter
, but you’re also trying to use it at the same time.
03:11
That’s where we get the UnboundLocalError
. You would like to let your program know that this counter
should be the same counter
that you defined in the outer function, in the enclosing scope.
03:24
And the way to do that is to use the nonlocal
keyword. The enclosing scope is also called the non-local scope and the nonlocal
keyword let’s Python know that you want to use the counter
that’s defined in the non-local scope or the enclosing scope.
03:42
And when you run this code, now you don’t get any errors because counter
is no longer a local variable within inner
.
03:51
Now there’s still the issue that the variable counter
is not accessible in the global scope. It’s accessible in the enclosing scope and in the local scope of inner
, but not in the global scope.
04:03
Let’s look at another alternative, how you can make it available in the global scope. Within print_with_counter()
, the outer function, you can define another inner function.
04:14
You can call this get_counter()
.
04:18
And this function returns the variable counter
.
04:22
You don’t need to use the nonlocal
keyword in this case, because you’re not making changes to counter
, you’re simply accessing it.
04:28
Accessing the variable from the enclosing scope is perfectly fine. It’s when you want to make changes to it, such as reassigning a value to it that you have to use the nonlocal
keyword.
04:39
However, this still creates a new local name, the name of the function get_counter()
, which is still local to the outer function print_with_counter()
. However, the outer function is returning inner
, so you can attach a new data attribute to inner
.
04:55
You can call it anything you want, but let’s still call it get_counter
. And this attribute is equal to the inner function get_counter
.
05:04
And since inner
is returned to the global scope, you’ll be able to access get_counter
using the attribute inner.get_counter
.
05:15
Let’s try this out. In the global scope, inner
does not exist, but print_
is the name that refers to this inner function. So let’s use the standard print()
this time to print print_.
And get_counter
is a function, so you need to add the parentheses to call it.
05:36
And when you run the script, you see that the two calls, the print_
print the string, but the final print shows you that the print_
function was called twice.
05:49
And you can test this further by calling print_
again. Since you’re using *args
and **kwargs
, you can put in several arguments and even keyword values such as the separator.
06:04
And after you call print_
again, you can once again display the output from print_
.get_counter
.
06:17
And this time after printing 3, 4, 5
get_counter
returns the value 3
since print_
has now been called three times.
Become a Member to join the conversation.