Environments and Scope

When Python is running your program, it does so in an isolated way, such that your program is not allowed to mess with other programs that may be running on your machine. Python does this by giving each program its own environment, it's own universe to exist in.

What is Scope?

Consider the following program:
def f(x):
a = 4
return 2 * x
Executing this throws an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
Wait, what? Didn't we define a right there?
This discrepancy is to do with how variables are "scoped" in Python. When a variable is defined in a function, it is accessible within that function, and any of its child functions, but not to any functions outside that or even the file outside the function, as seen above. In our given example, the scope of the variable a is limited to the function f.

What are Frames?

Frames are how Python implements the idea of scopes. Frames are also what you've been playing around with in PythonTutor, if you've had a chance to visit that website yet.
Think of it this way: every time Python defines a function call, it makes the "frame" in which it was defining the function the parent. Every time a function is called, a new "frame" is opened, that has access to everything within itself, and within its parent, and within its grandparent, and so on.
Consider the following example:
a = 6
def f(x):
b = a + x
def g(y):
return x + y + a
return g
func = f(5)
In this code-block, first we create the "global frame". This is the outermost layer, created by default when a program is run. In this global frame, we define the function f. So f's parent is the global frame. When we make the function call f(5), we open a new frame — a sub-universe. In this frame, we can still access our parent's variables, so a is still accessible.
We create g within f, which means that g's parent is f. This is why, when g is called, we can access the value of the variable x, which was defined in f. Note how we can also access a in g, because a is defined in the parent of the parent's frame — the global frame.
I hope that this section gave you a little bit of insight into how Python scopes variables. If this section was confusing, don't worry! It introduces a lot of new concepts very quickly, and might take some time to fully grasp. For a slower introduction to this concept, go ahead and read the next chapter — which is all about the intricacies of Python frames.