Scope Explained – Python

Python Scope Hierarchy

Understanding basic concepts when working with any programming language gives you great confidence in working with it. Understanding how to access different types of variables is crucial for proper variable usage and avoiding naming conflicts. Scope in Python determines the accessibility and visibility of variables, functions, and objects by following certain scope rules.

Before proceeding to Scope let me give an overview on the following

  • What are variables?
  • What are references to an object?
  • How python binds a variable to an object reference?

Variables, Object References & Binding

Let us start exploring these terms using a simple example.

physicist = "Richard Feynman"

What are variables?

Variables are nothing but a given name used to refer to an object. An object created based on the type of the value and the reference binds to the variable name.

In the above example physicist is the variable and Richard Feynman is the value and the object of type string referred by the variable name.

Rules for naming a variable.
  • A variable name may start with a alphabetic letter or an underscore.
  • A variable name may contain alpha numeric and only underscore characters.
  • A variable name may not contain special characters.
  • A variable name may not start with a number.

What is reference to an object?

In our example, it creates a string object that holds the value Richard Feynman. Reference to an object means this variable physicist is referring to a specific location in memory that holds the value.

How a variable binds to object reference?

And thus, python binds that name to the reference of an object using assignment operator (=). Python binds the variable name physicist with the reference of the string object – value being “Richard Feynman” – in our case.

Re-assigning the same variable name to a new object reference will change the binding to that new object. What we are actually doing is actually re-binding the reference of the old object to the reference of the new object created.

Let us now see how the bindings stored and accessed from different levels of scopes.


Scope and Types Of Scopes

In Python, there are four different types of scopes: local, enclosing, global, and built-in. Each scope can be thought of as a namespace or a dictionary-like object that stores and provides access to names (variables, functions, etc.).

  • Local specific to a particular function or code block.
  • Enclosing surrounds and includes nested functions or code blocks.
  • Global covers the entire module or program.
  • Built-In contains pre-defined functions and objects provided by Python.

In this hierarchy, each scope acts as a separate dictionary-like object, allowing names to be stored and accessed within their respective scopes. The local scope can access names from the enclosing scope, the enclosing scope can access names from the global scope, and the global scope can access names from the built-in scope.

The LEGB rule ensures that names looked up in the correct scope context, allowing for proper access and management of variables throughout the program. It follows the order of searching from local scopes, then enclosing scopes, followed by the global scope, and finally the built-in scope. This systematic search ensures that names will resolve and used appropriately within their respective scopes.

Python does not introduce a new nested scope for a variable defined in any code blocks like for loops or similar indentation blocks.

Let’s take each scope in python in turn:

Local Scope

Variables defined in a part of a python function and accessible within that current function refers to Local Scope. Variables defined in this local scope are Local Variables and cease to exist once exited from the current function.

def my_function():
    x = 10  # Local variable within the function
    print("Output:", x)

my_function()

Output: 10 # Prints this output.

Enclosing Scope

The enclosing scope, also known as the outer scope or the containing scope, refers to the scope that surrounds and encompasses a nested function or code block in Python. It is the scope that provides access to variables, functions, and other objects from the outer context.

Local Function

A function defined within the scope of another function is termed as Local function. It is a function that is only accessible and visible within the enclosing function.

Below is a simple example of it:

def outer():
    def inner():
        print("This is a local function.")

    inner()  # Call the local function

outer()

Local function is executed when module level functions are executed at run time. Local function bound by function definition executes every time when outer function is called just like any other name bound in a function body.

The nonlocal keyword in Python is used to access and modify a variable that is defined in the enclosing scope of a nested function. Here’s a simple example that shows how nonlocal works:

def outer():
    x = 10

    def inner():
        nonlocal x
        x = 20
        print("Inner:", x)

    inner()
    print("Outer:", x)

outer()

# Below is the Output
Inner: 20
Outer: 20

In this example, the outer function defines a variable x with an initial value of 10. Inside the inner function, we want to modify the value of x. Without using nonlocal, the x inside inner would be treated as a new local variable, and any modifications would not affect the x in the outer scope.

By adding the nonlocal keyword before x in the inner function, we indicate that x refers to the variable in the nearest enclosing scope (i.e., the outer function). This allows us to modify the value of x in the outer scope from within the nested inner function.

Global Scope

The global scope in Python refers to the top-level scope or the outermost scope in a program. The scope where variables, functions, and objects defined outside of any function or code block. Import statements, function or class definitions introduce module level scope bindings. We can also define other objects in the module scope which are constants or they can be variables as well.

Rebinding Global Name in current function scope

If you want to modify a global variable within a function, or you want to rebind a local variable with global variable you need to declare it as global to indicate that you are referring to the global variable and not creating a new local variable with the same name.

Here’s an example to illustrate the global scope in Python:

x = 10 # Global variable
y = 10 # Global variable

def my_function():
    global y  # Declare y as a global variable
    y = 20
    print("Inside the function:", x + y)

print("Value of Y before calling my_function(): ", y)
print("Before invoking the my_function():", x + y)
my_function()
print("Outside the function:", x + y)

# Output
Value of Y before calling my_function():  10
Before invoking the my_function(): 20
Inside the function: 30
Outside the function: 30

The global scope is important for defining variables and objects that need to be accessible globally throughout the program. However, use of global variables sparingly and preferring local variables within functions to maintain better code organization and avoid unintended side effects will be recommended.

Built-In Scope

The built-in scope in Python refers to the pre-defined scope that contains built-in functions, exceptions, and other objects that are available to use without the need for importing any modules. This scope includes functions like print(), len(), range(), and objects like Exception and TypeError.

The built-in scope in Python is automatically available to all Python programs without any explicit importing. It provides a set of commonly used functions, objects, and exceptions that facilitate various programming tasks.

However, it’s important to note that you can also define variables or functions with the same names as the built-in functions or objects, which can lead to confusion and unexpected behavior. Therefore, generally recommended to avoid using the names of built-in functions or objects for your own variables or functions to prevent any naming conflicts.

Conclusion

In Python, understanding scope is essential for effective programming. By comprehending the hierarchy of local, enclosing, global, and built-in scopes, developers can manage variables correctly and ensure their proper usage throughout their programs, preventing naming conflicts and promoting code clarity.


Leave a Reply