What Is OOP?
In short: object-oriented programming bundles data with the functions allowed to touch it, so the two stop drifting apart. A class is the blueprint; an object (an instance) is one thing built from it, with its own copy of the data. Keeping the rule next to the data gives everyone one obvious door to change it through — Python doesn't lock the other doors, it trusts you to use this one, and the rest of this section (encapsulation, inheritance, polymorphism, abstraction) is about making that trust harder to break. Reach for a class when several separate "things" each need to carry their own state and the rules for changing it.

A thermostat as a plain dict
A thermostat should never be set above 35°C. The obvious first version is a dict plus a function that checks before it writes:
That works — as long as everyone remembers to call set_temp. Nothing stops a
shortcut:
def set_temp(thermostat, temp):
if 0 <= temp <= 35:
thermostat["temp"] = temp
office = {"temp": 20}
set_temp(office, 22)
office["temp"] = 999 # a shortcut, somewhere else in the file
print(office)The dict and the check aren't connected — the connection lives in the programmer's head, and heads forget.
The same thermostat as an object
A class keeps the rule next to the data and gives everyone one obvious door to change
it through. Python doesn't lock the other doors — it trusts you to use this one (the
_name convention and properties in the encapsulation lesson make that trust harder
to break):
999 comes back as 35 — set_temp is the one place temp changes, so it's the one
place that has to get the rule right. Each Thermostat you make is independent, with
its own self.temp:
class Thermostat:
def __init__(self, temp=20):
self.temp = temp
def set_temp(self, temp):
self.temp = max(0, min(35, temp))
office = Thermostat()
lab = Thermostat(15)
office.set_temp(28)
print(lab.temp)A fleet of thermostats
Because every Thermostat carries the same shape, a list of them can be treated
uniformly — a loop doesn't need to know how many there are or care about each one's
history:
The old habit is still tempting, though — reach in and set the attribute instead of calling the method that guards it:
One reading comes back an impossible 500°. Route it through set_temp instead of assigning temp directly.
What the next four lessons add
A class alone only buys you one place the rule lives. Four more ideas build on
that, each solving a problem this lesson's Thermostat already hints at:
- Encapsulation — stop code outside the class reaching
t.tempdirectly at all, so the guard can't be skipped even by accident. - Inheritance — a
SmartThermostatthat does everythingThermostatdoes, plus a schedule, without copyingset_temp. - Polymorphism — the fleet loop above already calls
.is_comfortable()without asking what kind of thing it's talking to; later lessons lean on that on purpose. - Abstraction — a contract that says every thermostat must define
set_temp, refused at creation time if one forgets, not discovered at 3am.
Python doesn't force object-oriented style. A script with three functions and a dict is often the right size for the job. Reach for a class once you notice the same shape repeating — a name, some state, a rule for changing it — for more than one instance of a thing.
Quick practice
class Thermostat:
def __init__(self, temp=20):
self.temp = temp
def set_temp(self, temp):
self.temp = max(0, min(35, temp))
t = Thermostat # forgot the ()
t.set_temp(5)class Thermostat:
def __init__(self, temp=20):
self.temp = temp
def set_temp(self, temp):
self.temp = max(0, min(35, temp))
t = Thermostat()
t.temp = 999
print(t.temp)TypeErrorRun it and read the error. Then fix the method's parameters.
🎯 Your turn
Define a Thermostat class with __init__(self, start=20) storing self.temp, and
adjust(self, delta) that changes temp by delta but clamps it to 0..35
(never lower, never higher). Then write final_temp(start, deltas): open a
Thermostat at start, apply each delta in deltas in order, and return the final
temp.
final_temp(20, [5, 5])→30final_temp(20, [-50])→0final_temp(20, [100])→35
Hint: self.temp = max(0, min(35, self.temp + delta)) inside adjust; build one
Thermostat, loop the deltas calling .adjust(d), then return .temp.
Press ▶ Run and hit ✓ Check. ✅
