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
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 |
The important distinction is between accessing a value and assigning to its name.
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.
- 1Define the outer variable
The program defines visits at the outermost level and gives it an initial value of 0.
- 2Declare the binding
Inside the function, global visits explicitly connects the function's name visits to the top-level variable.
- 3Assign the updated value
The function can now read the current top-level value and assign the increased value back to that same variable.
- 4Call 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 |
Prefer parameters and return values when they express the intended data flow clearly.
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
Common mistake Assuming that assignment inside a function automatically changes the top-level variable
What it looks likeA function assigns score = 10 while a separate top-level score already exists.
Why it failsPython localizes variables by default on assignment inside a function.
Do this insteadUse global score before the assignment if the function must modify the top-level score.
Common mistake Using global when the function only needs to read a top-level variable
What it looks likeA function returns the current value of score but never assigns to score.
Why it failsReading a global variable works without global.
Do this insteadOmit global when the function only reads the value.
Common mistake Reading and assigning to the same global name without declaring it
What it looks likeA function tries to increase score by reading score and then assigning a new value, but has no global statement.
Why it failsMixing reads and writes without global can cause
UnboundLocalError.Do this insteadDeclare the name as global before the read-and-write operation, or redesign the function to use a parameter and return value.
Common mistake Using global by default for ordinary data flow
What it looks likeA function changes shared state even though the caller could pass the current value and receive the updated value.
Why it failsGlobal state reduces code clarity and testability.
Do this insteadPrefer 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
- 1Assignment 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.