Concepts / Avoiding Global State in Functions

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.

  • Programming

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?

  • The top-level score
  • A local score used by the function
  • Both scores
  • Neither score
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.

same name, separate bindingscoretop-level value: 5scorefunction-local value: 10
Which binding changes when a function assigns to a name that also exists at the top level?
python

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 functionglobal statement required?Reason
Read a top-level variableNoReading a global variable works without global
Assign to a top-level variableYesAssignment is localized by default
Read and then assign to the same top-level variableYesThe function needs explicit global access before writing
python

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.

can accessrequires declarationRead scoreglobal not requiredglobal scoreread and write accessAssign scoreglobal required
Why can a function read a top-level variable without global but require global before assigning to it?

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.

python
Output
visits is now 2
reads and updatesvalue becomes 1reads and updatesvalue becomes 2visits0record_visitfirst callvisits1record_visitsecond callvisits2
How does the top-level value change across repeated function calls when the function uses global?

This 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 stateExplicit data flow
The function changes data held outside its parameter listThe function receives data through parameters
The result may remain in a shared outer-scope variableThe function returns the updated value
Code clarity and testability can be reducedThe input and output relationship is easier to see from the function interface
python

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

MEDIUM

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

  1. 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.