Tutorials
must read
python

Scope of Variables in Python

Learn what variable scopes are all about and get familiar with the 'LEGB' rule. You will also deal with scenarios where you'll get to see the global and nonlocal keywords in action.

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.

Variable

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

Variable Scope

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...

Local Scope

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.

Enclosing Scope

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 second_num from outer() (# Print statement 3). It is not defined within that function. However, you can access first_num from inner() (# Print statement 1), because the scope of first_num is larger, it is within outer().
This is an enclosing scope. Outer's variables have a larger scope and can be accessed from the enclosed function inner().

Global Scope

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

Built-in Scope

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 Rule

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 print(x) within 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

Remember the 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()
Hello 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.

Global Keyword

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()
Hi World

That worked! Let's move to the next scenario...

Scenario 2: Enclosing Scope

Here, we have a look at the outer() and inner() nested functions from the Enclosing Scope example. Let's try to change the value of first_num from 1 to 0 from within inner().

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.

Nonlocal Keyword

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!

Conclusion

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.