Demystifying Python Scopes (with Examples)
If you’re coming from a programming language other than Python, such as C++, Java or C#, you might not be aware of the differences in Python’s scoping. By scoping, I mean the visibility of a name (e.g., a variable or more generally, an object).
Note: This isn’t supposed to be a full guide to Python scopes. This would have required a much lengthier post. Instead my goal is to help you get a foothold on this topic, and a good starting point from which you can keep learning.
Other Languages
In languages such as C, C++, Java, C#, each variable is defined in its enclosing block — that is, the curly braces of a function, if, loop, try block, etc.
Let’s look at some examples:
In Java, this is an error, since n is already defined in the outer scope (the “main” function):
class HelloWorld {
public static void main(String[] args) {
int n = 1;
System.out.println(n);
if (true) {
int n = 2;
System.out.println(n);
}
}
}
// HelloWorld.java:10: error:
// variable n is already defined in method main(String[])
// int n = 2;
And the following is an error, since n is only defined in the inner scope (the “if” block) and is not visible outside of it:
class HelloWorld {
public static void main(String[] args) {
if (true) {
int n = 2;
System.out.println(n);
}
System.out.println(n);
}
}
// HelloWorld.java:10: error: cannot find symbol
// System.out.println(n);
Scoping in Python
If you’re new to Python you might be surprised to learn that this code is valid:
if condition:
n = 1
else:
n = 2
print(n)
Let’s understand why:
In Python, a name is visible, or defined at the block level. Let’s break this down:
A name is generally an object (in our examples the variables), and the full definition can be found under “Binding of names” in the Python docs.
Here is the definition of a “block” according to the the “Execution Model” section in the python docs:
The following are blocks: a module, a function body, and a class definition.
Notice how if, loops, try clauses and others are NOT part of this definition.
As always, things are never this simple, but this is the basic understanding you should have: The “block level” for us is either a function, a class or a module (a python file in simple terms).
An important thing to note is that if one of your code branches do not define your variable, you might be in trouble, so you might want to define your variable ahead of time anyway:
flag = False
if flag:
n = 1
else:
print('I did not define n here')
print(n)
# Exception has occurred: NameError
# name 'n' is not defined
All the fine details can be found on the Python docs page “Execution Model” which I recommend reading.
Leg up with LEGB
The last thing I want to mention is how Python resolves names in your code (For example, if you have the same name in different places in your code), which is generally referred to as the “LEGB rule”.
In simple terms it means that Python will look for names (and stops when it finds one) in this order:
- Local names — e.g., a local variable defined in a function.
- Enclosed names — e.g., a variable in a nested function — a function within another function.
- Global names — e.g., a globally defined variable.
- Built-in Python names: e.g., True, False, int, def and so on.
In the following example, you can see how my_var (2) in foo takes precedence over the global variable, such that it defines a new variable under foo and does not set the value of the global my_var (1)
my_var = 1 # (1) Global var
def foo():
my_var = 2 # (2) Local var
def bar():
my_var = 3 # (3) Enclosed var
According to the LEGB rule, this means that foo’s my_var (2), is a different variable than the global one.
If you wanted to set the global variable, you could use the ‘global’ keyword:
my_var = 1 # Global var
def foo():
global my_var
my_var = 2 # Sets the global var
Similarly, you can set the function variable (foo’s my_var2) from the nested function this way:
def foo():
my_var2 = 2
def bar():
nonlocal my_var2
my_var2 = 3 # Sets the function's my_var2
bar()
Remember, this post is really just the tip of the iceberg, and to understand all the details I recommend reading this beautifully detailed post in “Real Python”, about the LEGB rule.
Understanding Python Scoping
When I was getting started with Python (moving from C#, C++ and Java), I was confused by how scoping works. I hope that this helps you understand the basics of Python scoping and makes you want to dive deeper and learn all about it.