According to Python's documentation:
"A scope is a textual region of a Python program, where a namespace is directly accessible."
Directly accessible means that when you're looking for an unqualified reference to a name, Python tries to find it in the namespace.
Scopes are determined statically, but actually, during runtime, they are used dynamically. This means that by inspecting the source code, you can tell what the scope of an object is, but this doesn't prevent the software from altering that during runtime. There are four different scopes that Python makes accessible (not necessarily all of them are present at the same time, of course):
- Thelocal scope, which is the innermost one and contains the local names.
- Theenclosing scope, that is, the scope of any enclosing function. It contains non-local names and also non-global names.
- Theglobal scope contains the global names.
- Thebuilt-in scope contains the built-in names. Python comes with a set of functions that you can use in an off-the-shelf fashion, such asprint,all,abs, and so on. They live in the built-in scope.
The rule is the following: when we refer to a name, Python starts looking for it in the current namespace. If the name is not found, Python continues the search to the enclosing scope and this continues until the built-in scope is searched. If a name hasn't been found after searching the built-in scope, then Python raises aNameError exception, which basically means that the name hasn't been defined (you saw this in the preceding example).
The order in which the namespaces are scanned when looking for a name is therefore:local,enclosing,global,built-in (LEGB).
This is all very theoretical, so let's see an example. In order to show you local and enclosing namespaces, I will have to define a few functions. Don't worry if you are not familiar with their syntax for the moment. We'll study functions inChapter 4,Functions, the Building Blocks of Code. Just remember that in the following code, when you seedef, it means I'm defining a function:
# scopes1.py
# Local versus Global
# we define a function, called local
def local():
m = 7
print(m)
m = 5
print(m)
# we call, or `execute` the function local
local()
In the preceding example, we define the same namem, both in the global scope and in the local one (the one defined by thelocal function). When we execute this program with the following command (have you activated your virtualenv?):
$ python scopes1.py
We see two numbers printed on the console:5 and7.
What happens is that the Python interpreter parses the file, top to bottom. First, it finds a couple of comment lines, which are skipped, then it parses the definition of the functionlocal. When called, this function does two things: it sets up a name to an object representing number7 and prints it. The Python interpreter keeps going and it finds another name binding. This time the binding happens in the global scope and the value is5. The next line is a call to theprint function, which is executed (and so we get the first value printed on the console:5).
After this, there is a call to the functionlocal. At this point, Python executes the function, so at this time, the bindingm = 7 happens and it's printed.
One very important thing to notice is that the part of the code that belongs to the definition of thelocal function is indented by four spaces on the right. Python, in fact, defines scopes by indenting the code. You walk into a scope by indenting, and walk out of it by unindenting. Some coders use two spaces, others three, but the suggested number of spaces to use is four. It's a good measure to maximize readability. We'll talk more about all the conventions you should embrace when writing Python code later.
What would happen if we removed thatm = 7 line? Remember the LEGB rule. Python would start looking form in the local scope (functionlocal), and, not finding it, it would go to the next enclosing scope. The next one, in this case, is the global one because there is no enclosing function wrapped aroundlocal. Therefore, we would see two numbers5 printed on the console. Let's actually see what the code would look like:
# scopes2.py
# Local versus Global
def local():
# m doesn't belong to the scope defined by the local function
# so Python will keep looking into the next enclosing scope.
# m is finally found in the global scope
print(m, 'printing from the local scope')
m = 5
print(m, 'printing from the global scope')
local()
Runningscopes2.py will print this:
$ python scopes2.py5 printing from the global scope5 printing from the local scope
As expected, Python printsm the first time, then when the functionlocal is called,m isn't found in its scope, so Python looks for it following the LEGB chain untilm is found in the global scope.
Let's see an example with an extra layer, the enclosing scope:
# scopes3.py
# Local, Enclosing and Global
def enclosing_func():
m = 13
def local():
# m doesn't belong to the scope defined by the local
# function so Python will keep looking into the next
# enclosing scope. This time m is found in the enclosing
# scope
print(m, 'printing from the local scope')
# calling the function local
local()
m = 5
print(m, 'printing from the global scope')
enclosing_func()
Runningscopes3.py will print on the console:
$ python scopes3.py(5, 'printing from the global scope')(13, 'printing from the local scope')
As you can see, theprint instruction from the functionlocal is referring tom as before.m is still not defined within the function itself, so Python starts walking scopes following the LEGB order. This timem is found in the enclosing scope.
Don't worry if this is still not perfectly clear for now. It will come to you as we go through the examples in the book. TheClasses section of the Python tutorial (https://docs.python.org/3/tutorial/classes.html) has an interesting paragraph about scopes and namespaces. Make sure you read it at some point if you want a deeper understanding of the subject.
Before we finish off this chapter, I would like to talk a bit more about objects. After all, basically everything in Python is an object, so I think they deserve a bit more attention.