Common Python Problems - [11] Misunderstanding Python scope rules

By JoeVu, at: Jan. 20, 2023, 12:39 p.m.

Estimated Reading Time: 6 min read

None
None

One of the key features of Python is its scope rules, which determine how variables are accessed and modified within a program. However, Python's scope rules can be a source of confusion for many developers, leading to errors and unexpected behavior. In this article, we will explore some common misunderstandings of Python scope rules and provide examples of how to properly use them.

1. Overview of Python Scope Rules

Python has four types of scopes, also known as namespaces 

  • The built-in namespace: contains all the built-in functions and constants that are available in Python
  • The global namespace: contains variables that are defined outside of any function or class, and are accessible from anywhere in the program. 
  • The enclosing namespace: contains variables that are defined in an outer function and are accessible in an inner function
  • The local namespace: contains variables that are defined within a function and are only accessible within that function.

When a variable is accessed or modified, Python searches these namespaces in a specific order to determine the variable's value.

Python follows the LEGB rule for namespace lookup: Local, Enclosing, Global, and Built-in. When a variable is referenced in a function, Python first searches the local namespace, then the enclosing namespace, then the global namespace, and finally the built-in namespace. If the variable is not found in any of these namespaces, a NameError is raised.


2. Misunderstandings of Python Scope Rules


2.1 Misunderstanding 1: Global Variables Can Be Modified from Within a Function

One common misunderstanding of Python scope rules is that global variables can be modified from within a function. While global variables can be accessed from within a function, they cannot be modified directly. Instead, the global keyword must be used to indicate that the variable should be treated as a global variable and modified accordingly.

For example, consider the following code:

x = 0

def increment():
    x += 1
    print(x)

increment()


This code will raise a UnboundLocalError: local variable 'x' referenced before assignment. The reason for this error is that Python assumes that x is a local variable because it is being modified within the function. To fix this, we can add the global keyword to indicate that x is a global variable:

x = 0

def increment():
    global x
    x += 1
    print(x)

increment()


Now, the output will be 1 because x is properly treated as a global variable and incremented.
 

2.2 Misunderstanding 2: Variables in a Loop Are Local Variables

Another common misunderstanding of Python scope rules is that variables defined in a loop are local variables. However, this is not the case. Variables defined in a loop are actually considered to be in the enclosing namespace, and are accessible from within any nested functions.

For example, consider the following code:

for i in range(5):
    def print_i():
        print(i)
    print_i()

This code will output 0 1 2 3 4 because i is considered to be in the enclosing namespace and is accessible from within the nested function print_i().


2.3 Misunderstanding 3: Variables Defined in One Function Are Accessible in Another Function

A third common misunderstanding of Python scope rules is that variables defined in one function are accessible in another function. However, this is not the case. Each function has its own local namespace, and variables defined in one function are not accessible in another function unless they are passed as arguments.

For example, consider the following code:

def outer():
    x = 1
    def inner():
        print(x)
    inner()

outer()


This code will correctly print 1 because inner() has access to the enclosing namespace, which contains x. However, if we define a new function another_inner() and try to access x from within it, we will get a NameError:

def outer():
    x = 1
    def inner():
        print(x)
    def another_inner():
        print(x)
    inner()
    another_inner()

outer()


This code will raise a NameError: name 'x' is not defined because another_inner() does not have access to the local namespace of inner().

To fix this, we can pass x as an argument to another_inner():

def outer():
    x = 1
    def inner():
        print(x)
    def another_inner(x):
        print(x)
    inner()
    another_inner(x)

outer()


Now, the output will correctly be 1 twice, once from inner() and once from another_inner().


3. Conclusion

Python's scope rules can be a source of confusion for many developers, but with a solid understanding of how they work, we can avoid common misunderstandings and write more reliable code. By remembering the LEGB rule and properly using the global keyword and function arguments, we can ensure that our variables are accessed and modified correctly within our programs.


Subscribe

Subscribe to our newsletter and never miss out lastest news.