Scope of Variables in Python
If you're familiar with Python or any other programming language, you'll undoubtedly know that variables need to be defined before they can be used in your program. In this tutorial, you will start with variable initialization. Next, you will get familiar with the boundary of variables within a program - its "scope". You will learn about the four different scopes with the help of examples: local, enclosing, global, and built-in. These scopes together form the basis for the LEGB rule used by the Python interpreter when working with variables. Then after, you will revisit some examples with additional complexity to open the way for the global keyword followed by the nonlocal keyword.
A variable is a label or a name given to a certain location in memory. This location holds the value you want your program to remember, for use later on. What's great in Python is that you do not have to explicitly state what the type of variable you want to define is - it can be of any type (string, integer, float, etc.). To create a new variable in Python, you simply use the assignment operator (
=, a single equals sign) and assign the desired value to it.
first_string_var = "First String" first_int_var = 1 total = 1 + 2 * 3
Assigning an initial value to a variable is called initializing the variable. You just initialized the variable:
first_string_var with a string value of First String and variable
first_int_var with an integer or numeric value of 1.
The part to the left of the assignment operator is the variable name, and the right side is its value. The right-hand side can also be an arithmetic operation - in which case, it will be evaluated before the assignment occurs.
Python has some rules that you must follow when creating a variable...
- It may only contain letters (uppercase or lowercase), numbers or the underscore character
- It may not start with a number.
- It may not be a keyword (you will learn about them later on).
If you do not follow these rules, you will get an error. Try it for yourself:
# No spaces are allowed in the variable first string value = "First string"
File "<ipython-input-1-2a2f77c0b400>", line 2 first string value = "First string" ^ SyntaxError: invalid syntax
# Cannot start with a number 1st_string_value = "First String"
File "<ipython-input-2-79b210888e10>", line 2 1st_string_value = "First String" ^ SyntaxError: invalid syntax
# Cannot be a keyword True = "True"
File "<ipython-input-3-5d4d2170e79c>", line 3 True = "True" ^ SyntaxError: can't assign to keyword
Now that you know how to initialize a variable. Let's talk about the scope of these variables. Not all variables can be accessed from anywhere in a program. The part of a program where a variable is accessible is called its scope. There are four major types of variable scope and is the basis for the LEGB rule. LEGB stands for Local -> Enclosing -> Global -> Built-in.
Let's learn more about scopes...
Whenever you define a variable within a function, its scope lies ONLY within the function. It is accessible from the point at which it is defined until the end of the function and exists for as long as the function is executing (Source). Which means its value cannot be changed or even accessed from outside the function. Let's take a simple example:
def print_number(): first_num = 1 # Print statement 1 print("The first number defined is: ", first_num) print_number() # Print statement 2 print("The first number defined is: ", first_num)
The first number defined is: 1 --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-10-17d580e01222> in <module> 6 print_number() 7 # Print statement 2 ----> 8 print("The first number defined is: ", first_num) NameError: name 'first_num' is not defined
We were able to print the
first_num variable by calling the function
print_number() (# Print statement 1). But when trying to access and then print the same variable from outside the function (# Print statement 2), it raised a
NameError. This is because
first_num is "local" to the function - thus, it cannot be reached from outside the function body.
What if we have a nested function (function defined inside another function)? How does the scope change? Let's see with the help of an example.
def outer(): first_num = 1 def inner(): second_num = 2 # Print statement 1 - Scope: Inner print("first_num from outer: ", first_num) # Print statement 2 - Scope: Inner print("second_num from inner: ", second_num) inner() # Print statement 3 - Scope: Outer print("second_num from inner: ", second_num) outer()
first_num from outer: 1 second_num from inner: 2 --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-4-13943a1eb01e> in <module> 11 print("second_num from inner: ", second_num) 12 ---> 13 outer() <ipython-input-4-13943a1eb01e> in outer() 9 inner() 10 # Print statement 3 - Scope: Outer ---> 11 print("second_num from inner: ", second_num) 12 13 outer() NameError: name 'second_num' is not defined
Got an error? This is because you cannot access
outer() (# Print statement 3). It is not defined within that function. However, you can access
inner() (# Print statement 1), because the scope of
first_num is larger, it is within
This is an enclosing scope.
Outer's variables have a larger scope and can be accessed from the enclosed function
This is perhaps the easiest scope to understand. Whenever a variable is defined outside any function, it becomes a global variable, and its scope is anywhere within the program. Which means it can be used by any function.
greeting = "Hello" def greeting_world(): world = "World" print(greeting, world) def greeting_name(name): print(greeting, name) greeting_world() greeting_name("Samuel")
Hello World Hello Samuel
This is the widest scope that exists! All the special reserved keywords fall under this scope. We can call the keywords anywhere within our program without having to define them before use.
Keywords are simply special reserved words. They are kept for specific purposes and cannot be used for any other purpose in the program.
These are the keywords in Python:
LEGB (Local -> Enclosing -> Global -> Built-in) is the logic followed by a Python interpreter when it is executing your program.
Let's say you're calling
inner(), which is a function nested in
outer(). Then Python will first look if "x" was defined locally within
inner(). If not, the variable defined in
outer() will be used. This is the enclosing function. If it also wasn't defined there, the Python interpreter will go up another level - to the global scope. Above that, you will only find the built-in scope, which contains special variables reserved for Python itself.
So far, so good!
Next, let's revisit some examples from before to see if they can create problems when the use-case becomes slightly more complex.
Scenario 1: Global Scope
greeting_world() function from earlier? Let's say you wanted to be able to change the global variable
greeting("Hello") to set a new value ("Hi") for the greeting, so that
greeting_world() prints "Hi World"
greeting = "Hello" def change_greeting(new_greeting): greeting = new_greeting def greeting_world(): world = "World" print(greeting, world) change_greeting("Hi") greeting_world()
Well...that did not go as wanted! Why?
This is because when we set the value of
greeting to "Hi", it created a new local variable
greeting in the scope of
change_greeting(). It did not change anything for the global
greeting. This is where the global keyword comes in handy.
With global, you're telling Python to use the globally defined variable instead of locally creating one. To use the keyword, simply type 'global', followed by the variable name. Let's see this in action on Scenario 1.
greeting = "Hello" def change_greeting(new_greeting): global greeting greeting = new_greeting def greeting_world(): world = "World" print(greeting, world) change_greeting("Hi") greeting_world()
That worked! Let's move to the next scenario...
Scenario 2: Enclosing Scope
Here, we have a look at the
inner() nested functions from the Enclosing Scope example. Let's try to change the value of
first_num from 1 to 0 from within
def outer(): first_num = 1 def inner(): first_num = 0 second_num = 1 print("inner - second_num is: ", second_num) inner() print("outer - first_num is: ", first_num) outer()
inner - second_num is: 1 outer - first_num is: 1
It isn't always that simple, is it! For such purposes, we make use of nonlocal keyword within Python.
This is another handy keyword that allows us to work more flexibly and tidily with variable scopes. The nonlocal keyword is useful in nested functions. It causes the variable to refer to the previously bound variable in the closest enclosing scope. In other words, it will prevent the variable from trying to bind locally first, and force it to go a level 'higher up'. The syntax is similar to the global keyword.
def outer(): first_num = 1 def inner(): nonlocal first_num first_num = 0 second_num = 1 print("inner - second_num is: ", second_num) inner() print("outer - first_num is: ", first_num) outer()
inner - second_num is: 1 outer - first_num is: 0
And there you have it!
You now know what Python's scope of variables is, the LEGB rule, and how you should use the global and nonlocal keywords. You'll be able to easily manipulate variables in nested functions, without any problem. To learn more about programming in Python, you should definitely take a look at DataCamp's Intro to Python for Data Science course. It is an interactive course that covers all the basics: from variables and calculations to lists, functions, and packages.