Start learning free

LESSON 556 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Thermostat in Python: A Private Target With Limits

Practice O(1) Read the full explanation

A thermostat keeps a target temperature, and the target must always stay between 10 and 30 degrees. If any code could change the target directly, nothing would stop it from setting 95. So the target is a private field: only the thermostat's own methods can change it, and every one of them keeps it between 10 and 30.

Implement the class Thermostat:

  • Thermostat() creates a thermostat whose target is 20.
  • setTarget(t) sets the target to t. A value below 10 sets it to 10, and a value above 30 sets it to 30.
  • warmer() raises the target by 1 and cooler() lowers it by 1, but neither ever takes it outside 10 to 30: at 30, warmer() leaves it at 30, and at 10, cooler() leaves it at 10.
  • getTarget() returns the target. It is the only way for other code to read it.

Keep the target private the way the Access Modifiers lesson does in your language: self._target in Python (private by convention), a #target field in JavaScript, and the private keyword in Java, C++, C#, TypeScript, Kotlin, Scala and Swift.

The input is a list of operations, applied in order to one new Thermostat. The output lists what the getTarget calls return, in order. The starter code spells these names the way your language does: set_target and get_target in Python, SetTarget, Warmer, Cooler and GetTarget in C#.

 

Example 1:

Input:
    getTarget()
    warmer()
    warmer()
    getTarget()
    cooler()
    getTarget()
Output: 20 22 21
Explanation: A new thermostat starts at 20. Two warmer() calls take it to 22, and cooler() brings it back to 21.

Example 2:

Input:
    setTarget(35)
    getTarget()
    warmer()
    getTarget()
    cooler()
    getTarget()
Output: 30 30 29
Explanation: 35 is above 30, so the target becomes 30. warmer() can't go past 30, so the target stays 30.
cooler() lowers it to 29.

Example 3:

Input:
    setTarget(4)
    getTarget()
    cooler()
    getTarget()
    setTarget(10)
    warmer()
    getTarget()
Output: 10 10 11
Explanation: 4 is below 10, so the target becomes 10, and cooler() can't go below 10. setTarget(10) keeps 10,
since 10 itself is allowed, and warmer() raises it to 11.

 

Constraints:

  • -1000 <= t <= 1000
  • A test case has from 1 to 1000 operations, and at least one of them is getTarget.
STEP 1 OF 7
    Ctrl + Enter to check

    PYTHON · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Thermostat in Python, explained

    Problem overview

    A thermostat keeps a target temperature that must always stay between 10 and 30 degrees. If any code could set the target directly, nothing would stop it from storing 95. So the target is kept inside the object, and only the thermostat's own methods change it. Every one of them keeps the target in range.

    • Thermostat() starts with a target of 20.
    • set_target(t) sets the target to t, but a value below 10 becomes 10 and a value above 30 becomes 30.
    • warmer() raises the target by 1 and cooler() lowers it by 1, never past 30 or below 10.
    • get_target() returns the target.

    For a new thermostat, two warmer() calls give 22 and a cooler() gives 21. After set_target(35) the target is 30, a warmer() leaves it at 30, and cooler() brings it to 29.

    Approach: one place for the rule

    The 10-to-30 rule is needed in three methods. Instead of writing it three times, write it once in set_target and let warmer and cooler go through it: warmer calls self.set_target(self._target + 1). At 30 that asks for 31, and set_target turns it back into 30. At 10, cooler asks for 9 and gets 10. If the range ever changes, there is a single place to edit.

    The field is named _target. In Python a leading underscore is a convention that means "private: use the methods, not this attribute". The language doesn't enforce it, so code outside the class can still write t._target = 95, and get_target() would then return 95. The convention is a promise between programmers, and a good class keeps it by offering methods for everything outside code needs.

    Step by step

    1. Store 20 in self._target in __init__.
    2. In set_target, handle a value below 10: an if whose body sets the target to 10. A value that is too low isn't refused. It becomes the lowest target allowed.
    3. Handle a value above 30 with an elif that sets the target to 30.
    4. Handle everything else with an else that sets the target to t. The values 10 and 30 themselves are allowed, so they end up here and are kept.
    5. warmer calls set_target with self._target + 1.
    6. cooler calls set_target with self._target - 1.
    7. get_target returns self._target. Nothing can change the target through it.

    The solution in Python

    class Thermostat:
        def __init__(self):
            self._target = 20
        def set_target(self, t):
            if t < 10:
                self._target = 10
            elif t > 30:
                self._target = 30
            else:
                self._target = t
        def warmer(self):
            self.set_target(self._target + 1)
        def cooler(self):
            self.set_target(self._target - 1)
        def get_target(self):
            return self._target
    

    A run:

    t = Thermostat()
    print(t.get_target())
    t.warmer()
    t.warmer()
    print(t.get_target())
    t.cooler()
    print(t.get_target())
    
    t = Thermostat()
    t.set_target(4)
    print(t.get_target())
    t.cooler()
    print(t.get_target())
    t.set_target(10)
    t.warmer()
    print(t.get_target())
    
    20
    22
    21
    10
    10
    11
    

    Why go through the methods

    The point of the exercise is that the rule lives in the class. A user of Thermostat never needs to check the range: whatever they call, the target stays valid, so every other piece of code can rely on it. That is the idea behind keeping a field private and exposing methods. The class guards its own data.

    A trace of warmer() at the top of the range shows the chain. The target is 30, so warmer calls set_target(31). Since 31 is above 30, the elif branch stores 30, and the target is unchanged. Nothing in warmer itself needed to know about the limit.

    Another way to clamp

    The three-branch if is the clearest form for a beginner. The same rule fits in one line with min and max: self._target = max(10, min(30, t)). The inner min caps the value at 30 and the outer max lifts it to at least 10. For the inputs 4, 10, 20, 30 and 35 it gives 10, 10, 20, 30 and 30, the same as the branches. Use whichever reads better to you, as long as the rule stays in one place.

    What "private" means in Python

    Many languages have a private keyword that the compiler enforces. Python doesn't. The underscore in _target tells other programmers to stay out, and that is all. The attribute is a normal one: after t = Thermostat(), hasattr(t, "_target") is True, while hasattr(t, "target") is False, so the name with the underscore is the real name. Keeping to the convention is what makes the class safe in practice: the tests use only the four methods.

    Edge cases and common mistakes

    • Exactly 10 or 30. Both are allowed, so they must reach the else branch. The tests are t < 10 and t > 30, strict on purpose.
    • Refusing instead of clamping. set_target(35) doesn't leave the old target in place. It sets 30.
    • Duplicating the rule in warmer and cooler. It works, but the three copies can drift apart. Going through set_target keeps one.
    • Writing to self._target from warmer directly as self._target += 1 skips the check, and the target would reach 31.
    • Far-out values. set_target(-1000) gives 10 and set_target(1000) gives 30, so the input can be anything.
    • Complexity. Each method does a few comparisons, so each is O(1).

    Testing

    • A new thermostat: get_target() returns 20.
    • set_target(30), then warmer(): the target stays 30.
    • set_target(10), then cooler(): the target stays 10.
    • set_target(35), then cooler(): the target is 29.
    Keep going with Python500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free