Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
73: The global and nonlocal Keywords
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)}")
There are no comments for now.