Understanding Scope in Python Programming (with Example Programs)

In previous articles, we learned about following topics:

Understanding Scope in Programming

  • The part of a program where a variable name can be accessed is called its scope.
  • Scope defines the area of the program in which a name can be accessible and different operations can be performed on that name.
    • Name can be of a variable, function, class or any object.
  • In the scope, we can use the given name to perform different operations like access, update and delete.
  • Not all variables are accessible from every part of the program.

Generally, in the scope of a program, a variable can be declared only once with the same name. But, it’s possible to declare variables with the same name in different scopes within the same program. Changing the value of a variable within a scope, does not impact its value outside that scope, even if the variables are of same name.

Scope of Code Blocks in Programming

  • Code block is defined as an area, which is separated from other sections or blocks of the program. For example: in a program, functions, classes, conditional statements and, loops are independent or separate code blocks.
  • In Python, all variables are scoped to the code block, it means
    • In the code block, the variable can be declared and assigned values.
    • Variables can be accessed, updated and deleted within that code block.
  • Python uses indentation to define a code block, it grouped the code under its parent statements like function, class, module or script file.

Understanding Variable Scope in Python

Python offers various levels of variable scope, each determining where a variable can be accessed and modified:

  1. Local Scope
  2. Global Scope (using the global keyword)
  3. Nonlocal Scope (using the nonlocal keyword)
  4. Enclosing Scope
  5. Built-In Scope
  • In Python, local scope” is the default scope, which also referred to as the “scope of the code block.
  • Unless otherwise specifically defined, all variables declared within a code block have local scope.

Python’s global keyword is used for modifying a variable’s scope when necessary. This “global” keyword is of significant use when working with scopes for a variable.

Variable Scope in Python
Variable Scope in Python

Local Scope in Python

Description of Local Scope:

  • Variables with local scope are declared inside a code block.
  • Variables declared within this code block have local scope, which is specific to that declaration block only.
  • Local variables cannot be accessed outside the block in which they were declared.

Program – Local Scope Example

Lets understand local scope in Python with a program.

def func_local_variable():
    shbytes_local = "This is local variable!!"
    print(shbytes_local)

func_local_variable()

print(shbytes_local)

Output of the local scope program

Traceback (most recent call last):
  File "D:\local_scope.py", line 7, in <module>
    print(shbytes_local)
          ^^^^^^^^^^^^^
NameError: name 'shbytes_local' is not defined
This is local variable!!

Let’s understand local scope with a program. Here, we define a variable named shbytes_local. This variable is declared inside the function func_local_variable(). This makes this variable local to this function, which means this variable can only be accessed within func_local_variable().

We print the shbytes_local variable’s value inside func_local_variable(), where its value is displayed. But when we are trying to print shbytes_local outside of the function, then it results in a NameError – indicating that the variable shbytes_local is not available outside its defined function.

Key Points from the above program:

  • By default, a variable has a local scope and is accessible only within its local scope.
  • The local scope is defined by the block or area where the variable is declared.
  • Attempt to access a variable outside of its scope will generate an error.

Examples of Local Scope in Python

Variables can be declared outside of any code block, which gives them a larger scope. This allows them to be used with in the sub-code blocks as well. Below are examples of local scope used with functions, classes and modules.

Variable Declared Directly inside a Function

  • Variables declared directly inside a function can be accessed inside the sub code blocks of that function.
  • These variables can be accessible inside the conditional statements and loops which are inside that function.
  • These variables can also be passed as arguments to other functions from within the defined function.

Program – Function Local Scope Variable

def local_function_scope_variable():
    shbytes_local = 0

    while(shbytes_local < 10):
        shbytes_local += 1
        if(shbytes_local %2 == 0) :
            print(f'{shbytes_local} is Even')
        else:
            print(f'{shbytes_local} is Odd')

local_function_scope_variable()

Program Output – Function Local Scope Variable:

1 is Odd
2 is Even
3 is Odd
4 is Even
5 is Odd
6 is Even
7 is Odd
8 is Even
9 is Odd
10 is Even

Variable declared directly inside a Class

  • Variables can be declared directly inside a Class, called as class level variables
  • These class level variables can be accessed directly inside the class
  • These variables can be passed as an argument to the functions using the class scope

Program – Class Local Scope Variable

class Shbytes:
    shbytes_local = 0
    print(shbytes_local)

    def func_scope_variable(number):
        while (number < 10):
            number += 1
            if (number % 2 == 0):
                print(f'{number} is Even')
            else:
                print(f'{number} is Odd')

Shbytes.func_scope_variable(Shbytes.shbytes_local)

Program Output – Class Local Scope Variable:

0
1 is Odd
2 is Even
3 is Odd
4 is Even
5 is Odd
6 is Even
7 is Odd
8 is Even
9 is Odd
10 is Even

Points to Note from the Program Example:

  • The variable shbytes_local is directly accessible within the Class where it was declared. This variable value can be printed directly from the Class.
  • This variable shbytes_local, can also be accessed using class name like Shbytes.shbytes_local and can be passed as a function argument.

Variables declared directly inside Module

  • Variables declared directly within a module are accessible throughout the module.
  • However, they are limited to the module boundary, meaning variables from one module cannot be used in another module.

Program – Module Local Scope Variable

shbytes_global = "This is global variable!!"

def func_global_variable():
    print(shbytes_global)

func_global_variable()
print(shbytes_global)

Program Output – Module Local Scope Variable

This is global variable!!
This is global variable!!

In the example, we defined a variable shbytes_global outside of any code block, directly within the module. This allows shbytes_global to be used anywhere within the module, and we demonstrate this by printing its value inside func_global_variable().

The global Keyword in Python

We can declare two different variables with the same name at different scope levels. For instance, a variable with name x can be declared both as a global variable and another variable x declared as local variable at different scope level.

But the same variable cannot have two scopes at the same time. A variable can only have either a global scope or local scope. The global keyword allows a local variable to be promoted to a global variable.

Program – global Keyword Example

def func_local_variable():
    global shbytes_local          # declared locally but made global
    shbytes_local = "This is local variable with global scope!!"
    print(shbytes_local)

func_local_variable()

print(shbytes_local)

In this example, the variable shbytes_local is defined within the function func_local_variable() but declared using the global keyword. Although the variable is defined locally, the global keyword allows this variable to be accessed outside of the function scope. When we print the value of shbytes_local outside the func_local_variable() function, the value can still be accessed and printed.

Program Output – global Keyword Example

This is local variable with global scope!!
This is local variable with global scope!!

If we hadn’t defined the variable shbytes_local with the global keyword, as done in the local scope example, it would have resulted in a NameError: name ‘shbytes_local’ is not defined.

Enclosing Scope in Python

Enclosing scope is significant while working with nested functions. In nested functions, variables declared in an outer function have a higher scope. This means, variables declared in an outer function, can be used in an inner function, but variables declared in an inner function, cannot be accessed in the outer function.

Due to this enclosing scope, variables declared in a function are only accessible within that function and its nested child functions.

Also, although variables declared in an outer function can be accessed in an inner function, any changes to the outer variable’s value within the inner function will remain local to the inner function and won’t affect the variable value in outer function.

  • A program always uses the variable, whose scope is closest to it.
  • This concept of enclosing scope does not apply to the nested structure of if statements and loops..

Program 1 – Enclosing Scope Example

def outer():
    outer_counter = 1

    def inner():
        inner_counter = 2
        print("outer counter : ", outer_counter)
        print("inner counter : ", inner_counter)
    
    inner()
    print("outer counter in func_outer : ", outer_counter)

outer()

Program 2 – Enclosing Scope Example

def outer():
    outer_counter = 1

    def inner():
        inner_counter = 2
        outer_counter = 3
        print("outer counter : ", outer_counter)
        print("inner counter : ", inner_counter)
    
    inner()
    print("outer counter in func_outer : ", outer_counter)

outer()
  • In Program 1, we are not updating the value of the variable outer_counter in the inner() function, but in Program 2, we are updating the value of the variable outer_counter in the inner() function.
  • In both programs, we can access the variable outer_counter inside the inner() function. However, in Program 2, when we re-declare the variable outer_counter inside the inner() function, this outer_counter variable will use it’s closest scope which is local scope. So, outer_counter variable defined within inner() function will be a separate variable with local scope.
  • In Program 2, although both variables share the same name, outer_counter, but they both are distinct variables.
  • The program will always use the variable closest to its scope.

Output of the above programs for enclosing scope

outer counter :  1
inner counter :  2
outer counter in func_outer :  1
outer counter :  3
inner counter :  2
outer counter in func_outer :  1
  • From the output of Program 1, variable outer_counter always refers to the variable declared in the outer() function, so its value is consistently 1.
  • In Program 2, within the inner() function, outer_counter refers to the variable with inner() function scope, whereas in the outer() function, outer_counter refers to the outer() function scope. Hence, their values are different => 3 and 1.

If we want to use the same variable declared in the outer() function without re-declaring it in the inner() function, we can use the nonlocal keyword.

nonlocal Keyword in Python

In the enclosing scope section, we have observed:

  • A variable with the same name declared inside an inner function is treated as a new variable with the inner function’s scope.
  • If we don’t want to declare another variable with the same name inside inner function but want to use the same variable declared in outer function, then we can use nonlocal keyword.

Properties of nonlocal Keyword

  • The nonlocal keyword was introduced from Python 3.
  • nonlocal keyword addresses the limitations of the enclosing scope.
  • Using nonlocal keyword, variables declared in outer function can be used in inner function and their values can be changed inside the inner function, which will be reflected to outer function as well.
  • The nonlocal keyword works only with variables declared in a nested function structure.
  • It cannot be used to change the variables declared with module scope.
  • In nested functions, nonlocal variables refer to the closest parent variable in the hierarchy and will take the first reference that it found in its parent scope.
  • If no variable reference exists in the parent hierarchy of nested functions, a SyntaxError is raised: “no binding for nonlocal ‘variable_name’ found”.

nonlocal Program to Understand Parent Hierarchy

Let’s understand how the reference for a nonlocal variable is taken from its parent hierarchy through an example.

  • If no variable is present in the parent hierarchy then SyntaxError will be raised.

Program 1

def nonlocalscope():
    nonlocal a
    a = "changing to non-local!"
    print(a)

a = "global variable!"
nonlocalscope()

Program 2

def localscope():
    a = "local variable!"

    def nonlocalscope():
        nonlocal a
        a = "changing to non-local!"
        print(a)
    nonlocalscope()
    print(a)

localscope()

In Program 1, we define a function named nonlocalscope() where we declare a nonlocal variable a. Since a is not declared within any parent hierarchy function, and variables declared at the module level can’t be assigned as nonlocal, it results in a SyntaxError – “no binding for nonlocal ‘a’ found.”

In Program 2, we use a nested function structure. Here, the variable a is declared in the outer localscope() function, and a nonlocal variable a is declared in its inner nonlocalscope() function. Using nonlocal in the inner function will not declare a new variable but will instead search for a in the parent scope. When it finds the reference in the parent localscope() function, it uses that reference.

By using the parent hierarchy reference, if we modify the variable value in the inner function, the change is reflected in the outer function as well—something that wasn’t possible with just the enclosing scope. If no reference for the variable a is found in its parent hierarchy, it raises a SyntaxError – “no binding for nonlocal ‘a’ found.”

Output of the above programs for enclosing scope

  File "D:\nonlocal_hierarchy.py", line 2
    nonlocal a
    ^^^^^^^^^^
SyntaxError: no binding for nonlocal 'a' found
changing to non-local!
changing to non-local!
  • From the Program 1 output, it throws a SyntaxError – “no binding for nonlocal ‘a’ found.”
  • From the Program 2 output, the value for variable a from the localscope() function is changed and printed twice.

nonlocal Program to Understand Its Scope

In this program, the same concepts are used as explained in the previous section.

Program 1

def func_outer():
    outer_counter = 1
    print("outer counter in func_outer : ", outer_counter)

    def func_inner():
        inner_counter = 2

        outer_counter = 3
        print("outer counter : ", outer_counter)
        print("inner counter : ", inner_counter)
    func_inner()
    print("outer counter in func_outer : ", outer_counter)


func_outer()

Program 2

def func_outer():
    outer_counter = 1
    print("outer counter in func_outer : ", outer_counter)

    def func_inner():
        inner_counter = 2
        nonlocal outer_counter
        outer_counter = 3
        print("outer counter : ", outer_counter)
        print("inner counter : ", inner_counter)
    func_inner()
    print("outer counter in func_outer : ", outer_counter)


func_outer()
  • In Program 1, the variable outer_counter is declared in the func_outer() function. We are using this variable inside the func_inner() function without the nonlocal keyword. This program’s output will be similar to the enclosing scope program output, as explained previously.
  • In Program 2, the variable outer_counter is declared in the func_outer() function, and we are using outer_counter variable inside the func_inner() function with the nonlocal keyword. In this case, the outer_counter variable will be referenced from the declaration in its parent func_outer() function.

Output of the above programs for nonlocal keyword

outer counter in func_outer :  1
outer counter :  3
inner counter :  2
outer counter in func_outer :  1
outer counter in func_outer :  1
outer counter :  3
inner counter :  2
outer counter in func_outer :  3

Built-in Scopes in Python

Python also has a built-in scope that does not apply to user-defined variables. Built-in scope is the widest scope and applies to all available special reserved keywords. We can call these keywords from anywhere in the programs or from any modules. These keywords are part of the Python core installation, reserved for specific purposes, and cannot be used for any other purpose in the program.

Reserved keywords are:

  • False
  • None
  • True
  • and
  • as
  • assert
  • break
  • class
  • continue
  • def
  • del
  • elif
  • else
  • except
  • finally
  • for
  • from
  • global
  • if
  • import
  • in
  • is
  • lambda
  • nonlocal
  • not
  • or
  • pass
  • raise
  • return
  • try
  • while
  • with
  • yield

Summary

This article explains the concept of scope in programming, focusing on variable scopes in Python. It defines scope as the context in which a variable is accessible, detailing the types of scopes in Python: local, global, nonlocal, enclosing and built-in. Local variables are confined to their specific code blocks, while global variables can be accessed throughout the module. The article also discusses the nonlocal keyword, which allows inner functions to reference and modify variables from outer functions. Through clear examples, readers will understand the significance of variable scope for effective code organization and data management in Python.

Python Code – Github Repository

Code snippets and programs related to Variable Scopes in Python, can be accessed from GitHub Repository. This GitHub repository all contains programs related to other topics in Python tutorial.

Interview Questions & Answers

What is the LEGB rule in Python?

LEGB rule is the order in which Python looks for a variables based on their scope. LEGB stands for:

  • Local – Local scope is the innermost scope, i.e., within the current function or code block.
  • Enclosing – This is the scope of any enclosing functions, in case of nested functions.
  • Global – Module-level scope or global scope
  • Built-in – The outermost scope, containing built-in names and functions.

In Python, reference to a variable is defined based on the LEGB order. Python will search these scopes in the LEGB order and will assign the first occurrence it finds.

What happens if we modify a global variable without the global keyword inside a function?

If we modify a global variable inside a function without using the global keyword, Python will consider it as a local variable within that function. This can lead to an error if the variable hasn’t been initialized within the function.

x = 10                     # variable defined at module level but not marked global

def modify_variable():
    x = 20                # This will create a new local variable 'x'
    print(x)              # This will print the value of local variable 'x'

modify_variable()             # Output: 20, from local variable 'x'
print(x)                               # Output: 10 (module level variable 'x' is unchanged)

Give a scenario where nonlocal is necessary?

nonlocal is necessary when you have a nested function and you want to modify a variable from the enclosing function (not the global scope).

def counter():
    count = 0                # Variable count is in the enclosing scope

    def increment():
        nonlocal count      # With nonlocal, it is using the variable from enclosing scope
        count += 1              # increase the value of enclosing scope variable count
        return count
    
    return increment

counter_function = counter()        # This will return the increment() function assigned to variable counter_function
print(counter_function())  # call to increment() function, Output: 1, initially count was 0
print(counter_function())  # call to increment() function, Output: 2, count was 1 after previous increment

What will happen if global keyword is used inside a function for a variable that is not defined in the global scope?

If global keyword is used for a variable that hasn’t been defined globally (or at module level with global keyword), Python will create that variable in the global scope. Python won’t raise an error.

We need to be very careful, while defining a global variable from inside the function is very risky and can lead to unexpected behavior in your code. Also use the proper comments in code for this usage of global keyword.

def create_global():
    global x                  # variable x defined in function but with global keyword
    x = 100                   # Assigns value to a global variable 'x'

create_global()
print(x)                      # Output: 100 from the global variable 'x'

How do Python closures relate to variable scope?

A closure in Python occurs when a nested function remembers the environment in which it was created, including variables from the enclosing scope, even after the outer function has finished executing. This is closely related to variable scope because closures allow the nested function to access variables from its enclosing scope, demonstrating the importance of the enclosing scope in the LEGB rule.

def outer_function(msg):
    def inner_function():
        print(msg)  # 'msg' is from the enclosing scope
    return inner_function

closure_function = outer_function("Hello, World!")
closure_function()  # Output: Hello, World!

In this example program, outer_function(msg) returns an object of inner_function which prints the value of argument msg. We called the outer_function("Hello, World!"). msg is remembered by inner_function even after outer_function has finished executing. This is because of closure.

Related Topics

  • Introduction to Variables in Python & Variable Data Types
    What are Variables in Python In Python, variables act as references to reserved memory locations where actual values are stored. Each variable name points to a specific memory address that holds the assigned value. In this case: How Variable Memory Works When we define variables, like height = 175, Python allocates a specific memory location…
  • Understanding Number Datatype in Python: Decimal, Octal and Hexadecimal Numbers
    Understanding the Number Datatype in Python: A Comprehensive Overview Number datatype in Python is a combination of different number format classes like Integer, Float, and Complex. Python supports the Decimal, Octal decimal, and Hexadecimal number systems. Program – Integer Number datatype Output of the Integer Number datatype program: Note from the output: Exploring Float Number…
  • Understanding Scope in Python Programming (with Example Programs)
    In previous articles, we learned about following topics: Understanding Scope in Programming Generally, in the scope of a program, a variable can be declared only once with the same name. But, it’s possible to declare variables with the same name in different scopes within the same program. Changing the value of a variable within a…
  • Understanding Lists in Python: A Comprehensive Guide
    Lists in Python List is a core datatype in Python. Lists are sequences used to store elements (collection of data). Lists are similar to Arrays but are of dynamic size. It means the size of the list can be increased or decreased as we add or remove elements from the list. Understanding Python Lists: A…
  • How to Create List in Python | Python List Creation Methods Explained
    List is a sequence data type used to store elements (collection of data). Lists are similar to Arrays but have a dynamic size, meaning the list size can increase or decrease as elements are added or removed. A list is a core data type in Python, along with Tuple, Set and Dictionary. Lists in Python…

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *