Python Learning – Variable Scope (Global and Local Variables)

The variable scope determines where a variable can be accessed within a program. Python has four main scopes, arranged in the order of lookup:

1. Four Levels of Scope (LEGB Rule)

  1. Local – Local Scope

  2. Enclosing – Enclosing Scope

  3. Global – Global Scope

  4. Built-in – Built-in Scope

2. Local Variables vs Global Variables

1. Local Variables

def calculate_sum(a, b):    # result is a local variable, accessible only within the function    result = a + b    return result
sum_result = calculate_sum(5, 3)print(sum_result)  # Output: 8# print(result)    # Error: NameError, result is a local variable

2. Global Variables

# Global variable, accessible throughout the moduleglobal_counter = 0
def increment_counter():    # To modify a global variable, use the global keyword    global global_counter    global_counter += 1
def read_counter():    # Reading a global variable does not require the global keyword    return global_counter
increment_counter()print(read_counter())  # Output: 1print(global_counter)  # Output: 1

3. Practical Examples of Scope

Example 1: Basic Scope

x = 10  # Global variable
def test_scope():    y = 20  # Local variable    print("Inside function - Local variable y:", y)    print("Inside function - Global variable x:", x)  # Can read global variable
test_scope()print("Outside function - Global variable x:", x)# print("Outside function - Local variable y:", y)  # Error: NameError

Example 2: Variable Shadowing

value = "global"  # Global variable
def demo_shadowing():    value = "local"  # Local variable, shadows the global variable    print("Inside function value:", value)  # Output: local
demo_shadowing()print("Outside function value:", value)  # Output: global

Example 3: Using the global Keyword

count = 0
def increment():    global count  # Declare to use the global variable    count += 1    print("Inside function count:", count)
increment()  # Output: Inside function count: 1increment()  # Output: Inside function count: 2print("Final count:", count)  # Output: 2

4. Enclosing Scope

def outer_function():    outer_var = "outer"  # Enclosing scope variable
    def inner_function():        inner_var = "inner"  # Local variable        print("Inner function access:", outer_var)  # Can access enclosing scope        print("Inner function variable:", inner_var)
    inner_function()    # print("Outer function access:", inner_var)  # Error: NameError
outer_function()

Using the nonlocal Keyword

def counter():    count = 0
    def increment():        nonlocal count  # Declare to use the enclosing scope variable        count += 1        return count
    return increment
my_counter = counter()print(my_counter())  # Output: 1print(my_counter())  # Output: 2

5. Built-in Scope

# Built-in functions and variablesprint(len([1, 2, 3]))  # len is a built-in functionprint(max(1, 5, 2))    # max is a built-in function
# Can override built-in functions (not recommended)def len(x):    return "Custom len function"
print(len([1, 2, 3]))  # Output: Custom len function
# Restore built-in function
del lenprint(len([1, 2, 3]))  # Output: 3

6. Best Practices for Scope

1. Avoid Overusing Global Variables

# Not recommendedglobal_data = []
def process_data():    global global_data    global_data.append("processed")
# Recommended approachdef process_data_better(input_data):    result = input_data.copy()    result.append("processed")    return result
data = []processed_data = process_data_better(data)

2. Use Function Parameters and Return Values

# Good design: pass data through parameters and return valuesdef calculate_stats(numbers):    """Calculate statistics"""    return {        'sum': sum(numbers),        'avg': sum(numbers) / len(numbers),        'max': max(numbers)    }
data = [1, 2, 3, 4, 5]stats = calculate_stats(data)print(stats)

3. Use Classes to Manage State

class Counter:    def __init__(self):        self.count = 0  # Instance variable
    def increment(self):        self.count += 1        return self.count
# Use my_counter = Counter()print(my_counter.increment())  # 1print(my_counter.increment())  # 2

7. Common Errors and Solutions

Error 1: Modifying a Global Variable Without Declaring Global

total = 0
def add_number(n):    # total += n  # Error: UnboundLocalError    global total  # Need to add this line    total += n
add_number(5)

Error 2: Redefining and Accessing External Variables Within a Function

x = 10
def confusing():    print(x)  # Error: UnboundLocalError    x = 20    # This line causes x to be treated as a local variable
# Fixdef clear():    global x    print(x)  # Normal    x = 20

8. Scope Lookup Order Verification

def test_legb():    # Verify LEGB lookup order    print("Looking up built-in function:", len)  # Built-in
    global_var = "global"
    def inner():        enclosing_var = "enclosing"
        def deepest():            local_var = "local"            print("Local:", local_var)            print("Enclosing:", enclosing_var)            print("Global:", global_var)            print("Built-in:", len)
        deepest()
    inner()
test_legb()

Conclusion

Scope Type Declaration Keyword Accessible Range Usage Scenario
Local Scope None Inside Function Temporary Variables, Function Parameters
Enclosing Scope nonlocal Between Nested Functions Closures, Decorators
Global Scope global Entire Module Configuration, Constants, Shared State
Built-in Scope None Everywhere Python Built-in Functions

Remember: Use local variables whenever possible, use global variables cautiously, and use enclosing scopes judiciously. Good scope management is key to writing clear and maintainable code.

Leave a Comment