Resource linked in this lesson: The LEGB Rule & Understanding Python Scope
Understanding Python Scope
00:00 Let’s make a start. What’s a closure? Now, definitions aren’t normally the best way to understand any topic, and this is particularly true for closures. However, let me still start with a definition of what’s a closure.
00:14 A closure is a function that retains access to its lexical scope, even when the function is executed outside that scope. Clear? I didn’t think so. This definition only makes sense if you already know what a closure is.
00:29 Since you’re following this course, I’ll assume you don’t know what a closure is, so let’s come back to this definition later in the course and it’ll make more sense at that stage.
00:39 So for now, let’s get rid of this. Instead, let’s look at a simpler definition, that of an inner function. An inner function is a function that you define inside another function.
00:50 Inner functions are essential in closures, and the best way to see how they’re used is by starting to write some code. Let’s have a look at an example.
01:01 To understand closures in Python, you first need to understand the concept of scope. Scope is that area of a computer program where names such as variable names or function names, are defined and exist.
01:14 And Python will look at four different levels of scope whenever it’s looking for a particular name. Let’s use this lesson to review the idea of scope to set the scene for discussing closures from the next lesson.
01:27
You can start by creating a script called exploring_scope.py
. And I’m going to start doing something that you’re not meant to do when you’re writing computer programs.
01:36
Let me define a variable called max
, and this is going to be equal to the string, “I was defined in the global scope.”
01:45
Why did I say you shouldn’t do this? Because max
is also a built-in name. There’s a built-in function max()
, which is the one you use to find the maximum value from a given sequence, for example.
01:55
So ideally, you shouldn’t shadow a name that already exists in the global scope, but in this case, I’m doing this to demonstrate the concept of scope. So the variable name max
is defined in the global scope.
02:07 This is the region of the program, which refers to the main entry point. In this case, you’ll be running the script so this is the main entry point of your program.
02:16
You’ll be able to understand the global scope better once we look at some other scopes. Next, you can define a function, and I’m going to call this function outer_function()
simply to clarify what it is.
02:28
There will be an inner function later on, so that’s why this one is called outer_function()
.
02:33
And within this function, I’m going to define a variable name called max
. And this time this is going to be a string, “Hi, I’m in the outer function.”
02:44
So now you have defined the variable name max
twice in your program, but they’re in different parts of the program. Let’s start by printing max
within the function, and then you can call outer_function()
from the main scope. You can run the code.
03:06
And as you can see, when you call outer_function()
, the call to print(max)
within the function uses the max
defined within the function.
03:13 It says, “Hi, I’m in the outer function.”
03:17
When you define the function in Python, it has its own scope. We call this the local scope within the function, and therefore, the max
you define within outer_function()
is a local variable.
03:29
It’s part of the local scope of the function. And when Python looks for max
, it first looks for local variables and only if it doesn’t find any local variable, it’ll look at the global variable.
03:41 However, there are more steps, and we’ll come to this in a bit.
03:46
So let’s now define an inner function within the outer_function()
.
03:53
And you can yet again define the variable max
. And this time it’s going to say, “Hi, I’m in the inner function,” of course, and print(max)
is now within the inner function.
04:08
Since inner_function()
is an inner function, in this case, you can simply call inner_function()
from within outer_function()
.
04:16
So inner_function()
is called within outer_function()
. And outer_function()
is called in the global scope in the main part of your program.
04:25
So when you call outer_function()
, you’re also calling inner_function()
.
04:30
So let’s have a look at what happens now when you run your exploring_scope.py
script. print(max)
, which is called within the inner_function()
, now returns the string, “Hi, I’m in the inner function,” and the argument is the same one as before because whenever Python is looking for a variable name, it first starts with the local scope if it’s within a function. And since there is a local variable max
defined within inner_function()
, Python will find that one first.
04:57 And that’s why it returns, “Hi, I’m in the inner function.”
05:01
But what happens if you comment out the definition of max
within the inner_function()
, however, you still keep print(max)
within inner_function()
.
05:10
So when Python runs inner_function()
, it’ll look for the name max
. It’ll start by looking in the local scope, but this time it’s not going to find it.
05:20
So the L, the local, has failed to find the name. Python next looks at another scope, and this is the enclosing scope. And the enclosing scope is the scope of the outer_function()
.
05:34
Why? Because this function is enclosed within an inner_function()
.
05:38
And since there is a max
in the enclosing scope, the one defined within outer_function()
, when you run your script, Python now returns, “Hi, I’m in the outer function.” So it looked for the local scope, it didn’t find it, it then looked in the enclosing scope and it found max
.
05:57
But let’s go a step further and you can comment out the max
you define in the outer_function()
as well.
06:04
A reminder, there’s still print(max)
in inner_function()
. So now when you call outer_function()
, which calls inner_function()
, Python will look for max
, it’s going to look in the local scope, it won’t find it.
06:15
It’s going to look in the enclosing scope, which is the local scope of the outer_function()
, but it still will not find max
. So now it’s going to look into the global scope.
06:25
And there you have defined max
. So when you run this script this time, Python will find max
. It’s going to find the one defined in the global scope, and that’s why it says, “I was defined in the global scope.” So you can now comment out the final definition of max
, the one you created in the global scope.
06:43
So now Python will not find max
in the local scope. It won’t find it in the enclosing scope, it won’t find it in the global scope either. But there’s one more scope for Python to look at, and this is the built-in scope.
06:58
And since there is a max
in the built-in scope, this is the built-in function max()
, the one you would use to find the maximum value.
07:05 For example, from a list of numbers, say. And this is the LEGB rule that Python uses to look for a variable name. It first looks at L local. If it doesn’t find it, it looks for E enclosing scope.
07:19
If it doesn’t find it there, it’ll look for the G global scope. And finally, if it doesn’t find it there either, it’ll look for the B, the built-in scope. Only if it doesn’t find a particular name in any of these scopes will Python raise a NameError
saying it cannot find a particular name.
07:38 So let’s review what you’ve learned about scope and the LEGB rule before moving back to closures. The scope of a name defines the area of a program in which you can unambiguously access the name. A name could be a variable name, a function, or any other object.
07:53 Python looks for a name in the following scopes if they exist, and in this order: the L stands for local scope, the E is the enclosing scope, the G is the global scope, and the B is the built-in scope.
08:07 You will always have a global and the built-in scope. However, the local scope is only present if you are referencing a name from within a function definition.
08:16 And the enclosing scope is only present if you define an inner function.
08:21 If you want to dive deeper into the topic of scope and the LEGB rule, then you can have a look at the Real Python video course called the LEGB Rule and Understanding Python Scope.
Become a Member to join the conversation.