Python Scoping Rules – A Simple Illustrated Guide

Introduction To Scope In Python

❖ What is Name in Python?

Everything in Python is an object. Since everything is an object we need to identify and distinguish each type of object from one another and this is what a name does. Name is simply a unique name given to objects in Python so that they can be identified. For example, when a function is defined in a program, it is given a name that is used to identify the function. Another example is a simple variable assignment var = 25. Here 2 is the object stored in the memory while var is the name given to the object.

❖ What is Scope?

The scope of a name like a variable, function, object, etc. is the region or part of the program where the name can be accessed directly. In other words, a name is visible and accessible only within its scope.

❖ What is Namespace?

A namespace is simply a collection of names. It is a container that contains the names mapped to their respective objects. Namespaces are isolated; thus same names within different scopes do not collide.

? The namespace that contains all the built-in names is created by default because of which built-in functions like print() can be accessed from all parts of the program.

❖ Example

name = "FINXTER"
print("Name in Global Scope: ", name)
def foo():
    name = "PYTHON"
    print("Name in Local Scope: ", name)
foo()

Output:

Name in Global Scope:  FINXTER
Name in Local Scope:  PYTHON

In the above example, we can see that the variable is in the global namespace as well as inside the local namespace and does not collide. Now that brings us to a very important discussion on variable scopes and the LEGB rule of scope resolution.

The LEGB Rule And Variable Scopes

LEGB is an abbreviation for Local(L)-Enclosed(E)-Global(G)-Built-In(B) and it is used to define Python Scope resolution. Let’s understand what is scope resolution and how LEGB works.

Disclaimer: The LEGB rules are specific to variable names and not attributes.

❖ Local Scope (L)

When a variable/name is created inside a function, it is only available within the scope of that function and ceases to exist if used outside the function. Thus the variable belongs to the local scope of the function. Every time the function is called, a new local scope is created. Local scope is also called function scope.

Example:

def foo():
  scope = "local variable"
  print(scope)
foo()

Output:

local variable

❖ Enclosing Scope (E)

An enclosing scope occurs when we have nested functions. When the variable is in the scope of the outside function, it means that the variable is in the enclosing scope of the function. Therefore, the variable is visible within the scope of the inner and outer functions. Enclosing Scope is often called non-local scope.

def foo():
  scope = "enclosed variable"
  def func():
    print(scope)
  func()
foo()

Output:

enclosed variable

In the above example, the variable scope is inside the enclosing scope of the function foo() and available inside the foo() as well as func() functions.

❖ Global Scope (G)

A global variable is a variable that is declared in a global scope and can be used across the entire program; that means it can be accessed inside as well outside the scope of a function. A global name is generally declared outside functions, in the main body of the Python code. In the backend, Python converts the programs main script into the __main__ module which is responsible for the execution of the main program. The namespace of the __main__ module is the global scope.

Example:

name = "FINXTER"
def foo():
    print("Name inside foo() is ", name)
foo()
print("Name outside foo() is :", name)

Output:

Name inside foo() is FINXTER
Name outside foo() is : FINXTER

❖ Built-In Scope (B)

The built-in scope is the widest scope available in python and contains keywords, functions, exceptions, and other attributes that are built into Python. Names in the built-in scope are available all across the python program. It is loaded automatically at the time of executing a Python program/script.

Example

x = 25
print(id(x))

Output:

140170668681696

❖ Example Of Scoping Rules In Python

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

Output:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200

The following diagram provides a pictorial representation of scopes rules in Python:

Understanding UnboundLocalError In Python

When a variable is assigned within a function, it is treated as a local variable by default in Python. If a local variable is referenced before a value has been assigned/bound to it, an UnboundLocalError is raised. In the above example when the variable 'val' is read by the Python interpreter inside the func() function, it assumes that 'val' is a local variable. However, it soon realizes that the local variable has been referenced before any value has been assigned to it within the function. Thus it throws an UnboundLocalError.

In other words, we can only access a global variable inside a function but cannot modify it from within the function (unless you force a global or nonlocal assignment using the global or nonlocal keywords).

Example:

val = 100
def func():
    val = val + 100
    print(val)
func()

Output:

Traceback (most recent call last):
  File "C:/Users/Shubham-PC/PycharmProjects/pythonProject1/main.py", line 9, in <module>
    func()
  File "C:/Users/Shubham-PC/PycharmProjects/pythonProject1/main.py", line 5, in func
    val = val + 100
UnboundLocalError: local variable 'val' referenced before assignment

Resolution: To resolve an UnboundLocalError when the local variable is reassigned after the first use, you can either use the global keyword or the nonlocal keyword. The global keyword allows you to modify the values of a global variable from within a function’s local scope while the nonlocal keyword provides similar functionality in the case of nested functions.

That brings us to a very important topic – global and nonlocal keywords.

The global And nonlocal Keyword In Python

❖ The global Keyword

We already read about the global scope where we learned that every variable that is declared in the main body and outside any function in the Python code is global by default. However, if we have a situation where we need to declare a global variable inside a function as in the problem statement of this article, then the global keyword comes to our rescue. We use the global keyword inside a function to make a variable global within the local scope. This means that the global keyword allows us to modify and use a variable outside the scope of the function within which it has been defined.

Now let us have a look at the following program to understand the usage of the global keyword.

def foo():
    global name
    name = "PYTHON!"
    print("Name inside foo() is ", name)
foo()
name = "FINXTER "+name
print("Name outside foo() is ", name)

Output:

Name inside foo() is  PYTHON!
Name outside foo() is  FINXTER PYTHON!

❖ The nonlocal Keyword

The nonlocal keyword is useful when we have a nested function, i.e., functions having variables in the enclosing scope. In other words, if you want to change/modify a variable that is in the scope of the enclosing function (outer function), then you can use the nonlocal keyword. If we change the value of a nonlocal variable the value of the local variable also changes.

Example:

def foo():
  a = 25
  print("Value of 'a' before calling func = ",a)
  def func():
    nonlocal a
    a=a+20
    print("Value of 'a' inside func = ",a)
  func()
  print("Value of 'a' after exiting func = ",a)
foo()

Output:

Value of 'a' before calling func =  25
Value of 'a' inside func =  45
Value of 'a' after exiting func =  45

❖ Global Keyword vs Nonlocal Keyword

Before concluding this article, let us have a look at the key differences between a global and nonlocal variable/keywords.

  1. Unlike the global keyword, the nonlocal keyword works only in Python 3 and above.
  2. The global keyword can be used with pre-existing global variables or new variables whereas the nonlocal keyword must be defined with a pre-existing variable.

Conclusion

In this article we learned the following:

  • What are names in Python?
  • What are namespaces in Python?
  • What are scopes in Python?
  • The LEGB Scope Resolution Rule in Python.
  • The UnboundLocalError.
  • The Global and Nonlocal Keyword.

Please subscribe and stay tuned for more articles!

Where to Go From Here?

Enough theory. Let’s get some practice!

Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.

To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

You build high-value coding skills by working on practical coding projects!

Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?

🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!