Skip to Content
Course content

73: The global and nonlocal Keywords

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember a junior developer I mentored a few years back who spent an entire afternoon chasing a bug that felt like a ghost in the machine. He was building a simple API rate-limiter and had defined a variable called request_count = 0 at the top of his script. Inside his handle_request() function, he tried to increment it with request_count += 1. To his absolute frustration, Python kept throwing an UnboundLocalError, claiming the variable was referenced before assignment. He kept pointing at the top of the file, saying, "But it's right there! It's defined right there!"

The issue was that Python's scoping rules are a bit more nuanced than in languages like C++ or Java. In Python, if you assign a value to a variable inside a function, Python assumes that variable is local to that function. The moment he wrote request_count += 1 (which is shorthand for request_count = request_count + 1), Python decided request_count was a local variable. Since he hadn't defined a local version before trying to add to it, the program crashed. This is where global and nonlocal come into play.

Telling Python to Look Outside with global

When you want to modify a variable that lives at the top level of your module (the global scope) from inside a function, you have to be explicit. If you're only reading the value, you don't need to do anything; Python will happily look outside the function to find it. But the second you try to change that value, you need the global keyword.

count = 0

def increment_global():
    global count
    count += 1
    print(f"Global count is now: {count}")

increment_global() # Works perfectly

I'll be honest with you: overusing global is usually a sign that your code is becoming "spaghetti." Relying on global state makes debugging a nightmare because any function anywhere in your program could be changing that value. Use it sparingly, usually for configuration settings or simple flags, but know it's there when you truly need it.

Handling Enclosing Scopes with nonlocal

Now, things get a bit more interesting when you have nested functions—a function inside another function. This is common when you're creating "closures," where an inner function remembers the state of its parent. If you try to modify a variable in that parent function (the "enclosing" scope), the global keyword won't help you because the variable isn't global; it's just "outside" the inner function.

This is where nonlocal comes in. It tells Python, "Don't make this a local variable, and don't look in the global scope either—look in the function immediately surrounding this one."

def create_score_tracker():
    score = 0  # Local to create_score_tracker, but enclosing for update_score

    def update_score(points):
        nonlocal score
        score += points
        return score

    return update_score

my_game_score = create_score_tracker()
print(my_game_score(10)) # 10
print(my_game_score(5))  # 15

In the example above, score isn't a global variable—it's tucked away inside create_score_tracker. Without the nonlocal keyword, update_score would try to create its own local score variable, fail to find a starting value for it, and crash. By using nonlocal, we're essentially creating a private state that only the update_score function can touch, which is a much cleaner pattern than using globals.




📋 Practical Task

Fixing the Broken Game State Tracker

You've been handed a piece of code for a simple game engine. The developer tried to implement a global system volume control and a local player-specific score tracker, but the code is crashing with UnboundLocalError.

Your task: Modify the code below by adding the global and nonlocal keywords in the correct places so that the volume can be updated globally and the player's score can be updated within their specific session.

system_volume = 50

def set_system_volume(new_volume):
    # FIX THIS: Needs to update the global system_volume
    system_volume = new_volume
    print(f"System volume set to {system_volume}")

def create_player_session(player_name):
    score = 0
    
    def add_points(amount):
        # FIX THIS: Needs to update the score in the enclosing scope
        score += amount
        return score
    
    return add_points

# Testing the fixes
set_system_volume(80)
player1 = create_player_session("Alice")
print(f"Alice's score: {player1(20)}")
print(f"Alice's score: {player1(15)}")
Rating
0 0

There are no comments for now.

to be the first to leave a comment.