Avoiding Global State in Functions
Python localizes variables by default on assignment, making it impossible to modify global variables from functions without the global statement.
The Assignment Trap
A function can read a variable defined at the program's outermost level without a special declaration. Assignment is different. Python localizes a variable by default when a function assigns to that name, so the assignment does not modify the top-level variable unless the function uses the global statement.
What do you think happens?
A top-level variable named score starts at 5. A function assigns score = 10 without using global. Which score does the assignment change?
Reveal answer
Answer: A local score used by the function
Python localizes variables by default when assignment occurs inside a function. Without global, the assignment does not modify the top-level variable.
Separate Bindings from One Name
Think of the top-level variable and the function's assigned variable as separate bindings. They may use the same name, but an assignment inside the function is localized by default. The function's assignment therefore does not change the value held by the top-level binding.
In this example, the function assigns to a localized score. The top-level score remains separate because the function did not declare score as global.
Reading Versus Writing
| Operation inside a function | global statement required? | Reason |
|---|---|---|
| Read a top-level variable | No | Reading a global variable works without global |
| Assign to a top-level variable | Yes | Assignment is localized by default |
| Read and then assign to the same top-level variable | Yes | The function needs explicit global access before writing |
show_score reads the top-level score without global. increase_score writes to score, so it declares score as global before assigning to it. The declaration creates an explicit binding between the function's name and the outer-scope variable, allowing both read and write access.
Making a Global Update
Updating a top-level counter
A top-level variable named visits starts at 0. A function should increase that same variable each time it is called.
Define the outer variable: The program defines visits at the outermost level and gives it an initial value of 0.
Declare the binding: Inside the function, global visits explicitly connects the function's name visits to the top-level variable.
Assign the updated value: The function can now read the current top-level value and assign the increased value back to that same variable.
Call the function repeatedly: Each call changes the shared top-level state, so the next call starts with the value produced by the previous call.
The function modifies the top-level visits variable rather than creating a separate function-local visits binding.
visits is now 2This is a practical use of global: the function intentionally changes a variable defined at the outermost level. The shared value persists between calls, which is why the second call can update the result of the first call.
Clarity Costs of Shared State
Although global can make an update possible, global state can make a program harder to understand. A function that reads or changes shared outer-scope data has data flow that is less visible at the function call. The caller may need to know which global variables the function reads or changes, even though those variables are not listed as parameters or return values.
| Shared global state | Explicit data flow |
|---|---|
| The function changes data held outside its parameter list | The function receives data through parameters |
| The result may remain in a shared outer-scope variable | The function returns the updated value |
| Code clarity and testability can be reduced | The input and output relationship is easier to see from the function interface |
The first function changes shared global state. The second function receives a value and returns an updated value instead. The second style makes the data entering and leaving the function explicit, which supports clearer code when shared state is not necessary.
Mistakes to Avoid
Assuming that assignment inside a function automatically changes the top-level variable
Python localizes variables by default on assignment inside a function.
Fix:
Use global score before the assignment if the function must modify the top-level score.Using global when the function only needs to read a top-level variable
Reading a global variable works without global.
Fix:
Omit global when the function only reads the value.Reading and assigning to the same global name without declaring it
Mixing reads and writes without global can cause UnboundLocalError.
Fix:
Declare the name as global before the read-and-write operation, or redesign the function to use a parameter and return value.Using global by default for ordinary data flow
Global state reduces code clarity and testability.
Fix:
Prefer passing data through parameters and return values when that expresses the operation clearly.
Practice the Decision
For each situation, decide whether the function needs global. Then decide whether using a parameter and return value would make the data flow clearer. Situation 1: A function only reads a top-level setting and uses that value in its result. Situation 2: A function must increase a top-level counter that persists across calls. Situation 3: A function receives a number, calculates an updated number, and does not need to preserve shared state.
Hints
- Separate reading from assigning.
- Look for the situation where the function must modify a top-level variable.
- Ask whether the updated value can be returned instead of stored globally.
- Situation 1 does not require global because the function only reads the top-level value.
- Situation 2 requires global if the function directly modifies the top-level counter.
- Situation 3 does not require global; parameters and a return value make the data flow explicit.
Working Rule
- Assignment inside a function is localized by default, so it does not modify a top-level variable with the same name. Reading a top-level variable does not require global, but writing to it does. The global statement explicitly connects the function's name to the outer-scope variable and permits read and write access. Use global only when shared state is genuinely needed; otherwise, prefer parameters and return values because they make data flow clearer and support better testability.
Key Takeaways
- Python localizes a variable by default when a function assigns to that name.
- A function can read a top-level variable without global.
- A function needs global before it can assign to a top-level variable.
- Mixing reads and writes without global can cause UnboundLocalError.
- Prefer parameters and return values when they make data flow clearer than shared global state.