Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Objects  ›  Lesson

What Is OOP?

Object Oriented Programming 14 minBundling data with the functions allowed to change it
Your task
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. Then build final_temp(start, deltas): open a Thermostat at start, apply each delta in order, and return the final temp. Example: final_temp(20, [5, 5]) -> 30.

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.

Loose data and loose functions on the left, with nothing stopping a function-less write to balance; an Account object on the right where the front door to balance is a method that can refuse

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:

python

That works — as long as everyone remembers to call set_temp. Nothing stops a shortcut:

What does this print?
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):

python

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:

What does this print?
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:

python

The old habit is still tempting, though — reach in and set the attribute instead of calling the method that guards it:

broken — fix 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.temp directly at all, so the guard can't be skipped even by accident.
  • Inheritance — a SmartThermostat that does everything Thermostat does, plus a schedule, without copying set_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

What happens when you run this?
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)
What does this print?
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)
broken — fix itTypeError

Run 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]) → 30
  • final_temp(20, [-50]) → 0
  • final_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. ✅

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upA switch that remembers

Define a Switch class starting off (self.on = False) with a flip() method that toggles it. Write flips_result(n) that creates a Switch, flips it n times, and returns whether it ends up on.

classesstate
DrillA tally that never forgets

Define a Tally class starting at 0 with an add(n=1) method that adds n to the running count. Write total_after(adds) that creates a Tally, calls add once per number in adds, and returns the final count.

classesstatemethods
BuildA stack that won't crash empty

Define a Stack class holding self.items = [], with push(item), pop() (removes and returns the top, or None if empty — never crashes) and peek() (returns the top without removing it, or None). Write stack_ops(ops): each op is a string, "push 5", "pop" or "peek". Return a list of the results from every pop/peek (skip push, it has no output).

classesstateguards
BossA machine that refuses short change

Define a VendingMachine class taking a price and a buy(amount) method: "dispensed" if amount >= price, else f"insert more: {shortfall:.2f}" where shortfall is price - amount rounded to 2 decimals. Write vend_results(price, payments) that creates one VendingMachine and returns the result of buy for each payment in order.

classesstateguardsf-strings
CapstoneShapes that each know their own area

Define Square (side) and Circle (radius), each with an area(self) method (math.pi * radius ** 2 for the circle). Write total_area(specs) where specs is a list of [kind, measurement] pairs like ["square", 3] or ["circle", 2]. Build the matching object for each spec, then return the sum of every .area(), rounded to 2 decimals. Once the objects exist, the summing itself must not check kind — only call .area().

classespolymorphismarithmetic
final_temp.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
final_temp(start, deltas) → intOpen a Thermostat at start, apply each delta in order, return the final (clamped) temp.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.