EE1021 Lecture 7 Worksheet — Classes, Objects, and Methods

Instructions

  • Predict object state before running code.
  • Keep the supplied public class and method names unchanged.
  • Store persistent instance state on self.
  • Do not request user input or print inside reusable methods.
  • Test at least two instances to detect accidental shared state.
  • Complete the eight core tasks before the optional extension.

1. Identify the OOP Vocabulary

Given:

class Sensor:
    def add_reading(self, value):
        self.readings.append(value)

t1 = Sensor()
t1.readings = []
t1.add_reading(23.5)

Identify the class, instance, data attribute, method, receiver object, and argument.

2. Convert Requirements into a Class Contract

A sensor has an identifier, measurement unit, and reading history. It can add a reading, report its latest reading, calculate its average, and produce a readable description.

In Python the constructor is the __init__ method; it runs automatically when an object is created.

List:

  1. constructor parameters;
  2. instance attributes;
  3. state-changing methods;
  4. query methods;
  5. special methods;
  6. empty-data behavior.

3. Initialize Independent State

Implement:

class Sensor:
    def __init__(self, sensor_id, unit):
        ...

Raise ValueError when either text argument is empty. Create a new readings list for every object.

Verify:

t1 = Sensor("T1", "C")
p1 = Sensor("P1", "kPa")
assert t1.readings is not p1.readings

4. Trace self

Predict the final state without running:

t1.add_reading(20.0)
p1.add_reading(100.0)
t1.add_reading(22.0)

For each call, identify which object becomes self and which list changes.

5. Implement Commands and Queries

Implement:

add_reading(value)
reading_count()
latest()
average()

add_reading raises TypeError("reading must be numeric") for a nonnumeric value. latest and average raise ValueError("sensor has no readings") when empty.

Classify each method as state-changing or state-reporting.

6. Implement Status Boundaries

Implement:

status(low, high)

Use the latest reading:

  • below low → "low";
  • above high → "high";
  • otherwise → "normal".

Raise ValueError("low must not exceed high") when low > high. Test values below, at, between, and above the boundaries.

7. Implement __str__

Return this exact format:

T1: 2 readings (C)

Explain why printing inside __str__ would violate its contract.

8. Engineering Mini-Problem: Resistor

Implement:

class Resistor:
    """Represent one resistor with a positive resistance."""

Contract:

  • constructor receives label and resistance_ohm;
  • nonpositive resistance raises ValueError;
  • current(voltage) returns voltage / resistance_ohm;
  • power(voltage) returns voltage**2 / resistance_ohm;
  • __str__ returns "R1: 220 ohm" for Resistor("R1", 220).

Create two resistors and verify that their state and calculations are independent.

Optional Extension: Load Sensor Data

Read sensor_test_data.txt, create one Sensor per identifier, and add each parsed reading to the correct object. Use a dictionary that maps sensor IDs to Sensor objects.

Do not add inheritance or class variables; those begin in Lecture 8.

Submission Checklist

  • OOP vocabulary is used accurately.
  • Constructor data is stored on self.
  • Mutable state is independent for every instance.
  • Commands change state and queries return values.
  • __str__ returns rather than prints.
  • Empty and invalid inputs follow documented contracts.
  • Two-instance and boundary tests pass.
  • The Resistor class is complete.