What Is State in a Program?
Layer 3 begins. Everything so far has been static โ built once and read. State is data that legitimately changes while a program runs, and tracking it correctly is a skill of its own.
Data that is expected to change while the program runs.
1. Learning Objective
By the end of this session you will be able to:
- Define "state" as data that changes over the lifetime of a running program
- Distinguish construction-time data (Session 15) from data that is expected to change afterward
- Explain why uncontrolled mutation of shared state causes bugs that are hard to trace
- Identify which attributes of Country and CountryExplorer are state versus fixed identity
- Recognise this as a concept-only session, mirroring Session 12's approach
2. Pre-Coding Quiz
Answer these before reading the concept explanation or writing any code. It is fine to get these wrong โ that is the point. You need 4/5 to proceed to the lab.
Which of these Country attributes is most clearly "state" โ data expected to change during the program's life โ versus a fixed identifying property?
A CountryExplorer's .countries list grows as more data loads in. Is that state?
Two different parts of a program both hold a reference to the same CountryExplorer instance. One part appends a country. What happens from the other part's point of view?
Why can uncontrolled, scattered mutation of shared state be hard to debug?
Why is this session mostly concept and almost no new syntax?
3. The Concept โ State
Construction sets the starting values. State is the subset expected to keep changing afterward, through controlled methods.
State is data that changes while the program runs
Every attribute we have written so far has technically been mutable โ but not every attribute is meant to change. "State" specifically means data that is expected and designed to change over the lifetime of a running program, in response to something happening.
class Country:
def __init__(self, name, region, population):
self.name = name # fixed identity โ not expected to change
self.region = region # fixed identity โ not expected to change
self.population = population # STATE โ expected to change over time
Construction-time data vs state
Session 15 established that data arrives explicitly through the constructor. Some of that data stays fixed for the object's lifetime (like name); other data is the object's starting state, expected to evolve afterward (like population, which grow_population already updates).
A collection can be state too
CountryExplorer.countries is state โ it grows as data loads, and potentially shrinks or reorders based on user actions later in the project.
class CountryExplorer:
def __init__(self, countries):
self.countries = countries # starts here, but this list is STATE โ it changes over time
explorer = CountryExplorer(countries=[])
explorer.countries.append(Country(name="Kenya", region="Africa", population=54000000))
# .countries just changed shape โ this is state changing over the program's lifetime
Why uncontrolled mutation is risky
If any part of a large program can reach in and mutate shared state directly, tracing an unexpected value back to its cause becomes very difficult โ there is no single place to look. The next several sessions build the discipline of changing state only through deliberate, well-named methods.
# Risky โ anyone, anywhere, can silently corrupt state
explorer.countries[0].population = -999999 # no validation, no traceability
# Better (Session 21 formalizes this) โ a controlled method with rules
explorer.countries[0].grow_population(1000000) # validated, named, traceable
4. Lab
What you will build
A file called state_concept.py โ mostly comments and small demonstrations, not new functionality.
Step-by-step instructions
Create the file and re-declare Country and CountryExplorer
# state_concept.py
class Country:
def __init__(self, name, region, population):
self.name = name
self.region = region
self.population = population
class CountryExplorer:
def __init__(self, countries):
self.countries = countries
Annotate each attribute with a comment: STATE or FIXED
This is the most important step โ write your reasoning, not just a label.
class Country:
def __init__(self, name, region, population):
self.name = name # FIXED โ identity, not expected to change
self.region = region # FIXED โ identity, not expected to change
self.population = population # STATE โ expected to change over time (Session 14)
class CountryExplorer:
def __init__(self, countries):
self.countries = countries # STATE โ grows/shrinks as data loads (Session 16)
Demonstrate uncontrolled mutation directly and observe it works with no guardrails
k = Country(name="Kenya", region="Africa", population=54000000)
k.population = -999999 # directly assigned, bypassing any validation
print(k.population) # -999999 โ nothing stopped this
Demonstrate two references sharing the same mutable state
Recall Session 05 โ prove it applies to real objects too, not just dicts.
explorer = CountryExplorer(countries=[k])
same_explorer = explorer # NOT a copy โ same reference
same_explorer.countries.append(Country(name="Ghana", region="Africa", population=31000000))
print(len(explorer.countries)) # 2 โ the change is visible through BOTH names
print(len(same_explorer.countries)) # 2
Write a short comment describing the risk you just observed
No new code required โ summarize, in your own words, why direct attribute assignment (step 3) is risky compared to a controlled method.
5. Expected Files Changed
| File | Action | Why |
|---|---|---|
state_concept.py |
Created | Identifies state vs fixed attributes and demonstrates uncontrolled mutation risk. |
docs/sessions/session-20/index.html |
Created | This session document. |
6. Commit Checkpoint
Once the lab is complete and you can explain every line, make this exact commit:
git add state_concept.py docs/sessions/session-20/index.html
git commit -m "session-20: identify state vs fixed attributes and observe uncontrolled mutation risk"
7. Code Review Checklist
Go through your code line by line and check each item:
8. Post-Coding Quiz
Same 5 questions. Take it again now that you have written and run the code. You need 4/5 to mark this session complete.
Which of these Country attributes is most clearly "state" โ data expected to change during the program's life โ versus a fixed identifying property?
A CountryExplorer's .countries list grows as more data loads in. Is that state?
Two different parts of a program both hold a reference to the same CountryExplorer instance. One part appends a country. What happens from the other part's point of view?
Why can uncontrolled, scattered mutation of shared state be hard to debug?
Why is this session mostly concept and almost no new syntax?
9. Reflection Questions
Think through these after the post-quiz. No right answer โ they are for discussion.
- Is region truly always fixed? Can you think of a real-world scenario (e.g. historical border changes) where it might legitimately need to be state instead?
- Why do you think this session deliberately shows you the RISK of uncontrolled mutation before Session 21 shows you the SOLUTION?
- How does "two references sharing mutable state" in this session's lab connect back to Session 05's object mutation, Session 13's independent instances, and Session 16's composition?
- Where in a large real application would uncontrolled state mutation be catastrophic versus merely annoying?
10. What Breaks If This Knowledge Is Missing?
- Untraceable bugs: A program where any code anywhere can mutate any state directly makes "why is this value wrong" a search through the entire codebase instead of a single, well-known method โ this is the single most common source of hard-to-debug real-world software issues.
- Controlled updates (Session 21): The next session channels all state changes through named, validating methods. Without today's clear sense of what counts as state, that discipline will feel like unnecessary ceremony instead of a direct fix for a problem you just observed yourself.
- Re-render logic (parallel to the original course's Session 23): This mirrors exactly why the source React course treats state as sacred โ uncontrolled mutation there breaks UI updates. Here, it breaks traceability. The underlying discipline is the same.
11. What We Learned
Python concept mastered: State โ data expected to change during a program's run, distinct from fixed identity data, and the risk of mutating it without a controlled entry point.
Unlocks: You can now identify state in any class you design, and you have directly observed why uncontrolled mutation is a real problem, not a theoretical one.
Next session: Session 21 โ Updating State via Methods. We formalize controlled state updates through named, validating methods โ turning the risk from this session into a solved problem.