====================================================
Exercises
====================================================
This page contains four take-home exercises that reinforce the concepts
from Lecture 6. Each exercise asks you to **write code from scratch**
based on a specification -- no starter code is provided.
All files should be created inside your ``lecture6/`` workspace folder.
.. dropdown:: Exercise 1 -- Building a Robot Class
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Demonstrate your understanding of class definitions, the ``__init__``
constructor, the ``self`` parameter, instance attributes, and class
attributes.
.. raw:: html
**Specification**
Create a file ``lecture6/robot_basics.py`` that implements the
following. Each class and method must include type hints and a
Google-style docstring.
1. **``Robot`` class** with:
- ``__init__(self, name: str, battery: int = 100)``
- Instance attributes: ``name``, ``battery``, ``tasks_completed``
(default ``0``)
- A class attribute ``total_robots`` that tracks how many Robot
instances have been created. Increment it in ``__init__``.
2. **``perform_task(self, task_name: str) -> None``** method that:
- If ``battery >= 10``: prints ``" performing: "``,
decreases battery by 10, and increments ``tasks_completed``.
- Otherwise: prints ``" needs recharging!"``.
3. **``recharge(self) -> None``** method that sets battery back to 100
and prints ``" fully recharged!"``.
4. In the ``if __name__ == "__main__"`` block:
- Create two Robot instances.
- Have each robot perform several tasks.
- Print each robot's ``tasks_completed`` and ``battery`` after the
tasks.
- Print ``Robot.total_robots``.
.. code-block:: python
if __name__ == '__main__':
# Create two Robot instances
robot1 = Robot("Atlas")
robot2 = Robot("Spot", 50)
# Have them perform several tasks
robot1.perform_task("welding")
robot1.perform_task("painting")
robot1.perform_task("inspection")
robot2.perform_task("delivery")
robot2.perform_task("sorting")
robot2.perform_task("scanning")
robot2.perform_task("lifting")
robot2.perform_task("packing")
robot2.perform_task("cleaning") # battery hits 0 after this
# This one should trigger the recharge warning
robot2.perform_task("assembly")
# Recharge and continue
robot2.recharge()
robot2.perform_task("assembly")
# Print final state
print(f"\n{robot1.name}: tasks={robot1.tasks_completed}, battery={robot1.battery}")
print(f"{robot2.name}: tasks={robot2.tasks_completed}, battery={robot2.battery}")
print(f"Total robots created: {Robot.total_robots}")
**Expected output:**
.. code-block:: text
Atlas performing: welding
Atlas performing: painting
Atlas performing: inspection
Spot performing: delivery
Spot performing: sorting
Spot performing: scanning
Spot performing: lifting
Spot performing: packing
Spot performing: cleaning
Spot needs recharging!
Spot fully recharged!
Spot performing: assembly
Atlas: tasks=3, battery=70
Spot: tasks=6, battery=90
Total robots created: 2
.. raw:: html
**Deliverables**
- ``lecture6/robot_basics.py``
- The program must run without errors and produce output matching the
expected format above.
.. dropdown:: Exercise 2 -- Dunder Methods for a Robot Class
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Practice implementing dunder methods for string representations,
comparison operators, arithmetic operators, and the ``__len__``
protocol.
.. raw:: html
**Specification**
Create a file ``lecture6/robot_dunders.py`` that implements the
following. Each class and method must include type hints and a
Google-style docstring.
1. **``Robot`` class** with:
- ``__init__(self, name: str, battery: int = 100)``
- Instance attributes: ``name``, ``battery``, ``tasks_completed``
(default ``0``)
2. **String representations:**
- ``__str__`` returns ``" [%]"``
- ``__repr__`` returns ``"Robot('', )"``
3. **Comparison operators:**
- ``__eq__`` so two robots are equal if their battery levels are
equal (regardless of name). Return ``NotImplemented`` for
non-Robot types.
- ``__gt__`` so robots can be compared by battery level. Return
``NotImplemented`` for non-Robot types.
4. **Arithmetic operator:**
- ``__add__`` so adding two Robots returns a new Robot with name
``"merged"`` and the sum of their batteries **capped at 100**.
5. **Length protocol:**
- ``__len__`` returns ``tasks_completed``.
6. In the ``if __name__ == "__main__"`` block, test all dunder
methods:
.. code-block:: python
if __name__ == "__main__":
scout = Robot("Scout", 60)
hauler = Robot("Hauler", 60)
# __str__: human-readable output
print(scout) # Scout [60%]
# __repr__: developer output, looks like valid Python
print(repr(scout)) # Robot('Scout', 60)
# __eq__: compare by battery level
print(scout == hauler) # True (both have battery 60)
print(scout == Robot("X", 90)) # False (different battery)
# __add__: merge two robots, battery capped at 100
print(scout + hauler) # merged [100%] (60 + 60 = 120, capped to 100)
print(scout + Robot("X", 20)) # merged [80%] (60 + 20 = 80, no cap needed)
# __gt__: compare by battery level
print(scout > Robot("X", 40)) # True (60 > 40)
print(scout > hauler) # False (60 > 60 is False)
# __len__: number of tasks completed
scout.tasks_completed = 3
print(len(scout)) # 3
print(len(hauler)) # 0 (default, no tasks performed yet)
**Expected output:**
.. code-block:: text
Scout [60%]
Robot('Scout', 60)
True
False
merged [100%]
merged [80%]
True
False
3
0
.. raw:: html
**Deliverables**
- ``lecture6/robot_dunders.py``
- The program must run without errors and produce output matching the
expected format above.
.. dropdown:: Exercise 3 -- Encapsulated Sensor
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Practice encapsulation with non-public attributes, ``@property``
decorators, read-only properties, and setter validation.
.. raw:: html
**Specification**
Create a file ``lecture6/sensor_encapsulated.py`` that implements the
following. Each class and method must include type hints and a
Google-style docstring.
1. **``Sensor`` class** with:
- ``__init__(self, sensor_type: str, range_m: float = 10.0)``
- Non-public attributes: ``_sensor_type``, ``_range_m``
2. **Read-only property** ``sensor_type`` that cannot be changed after
creation. The setter should raise ``AttributeError``.
3. **Property** ``range_m`` with a setter that:
- Raises ``ValueError`` if ``range_m`` is set to a negative number.
- Raises ``TypeError`` if ``range_m`` is not ``int`` or ``float``.
4. **Methods:**
- ``calibrate(offset: float) -> None`` adjusts ``_range_m`` by the
given offset.
- ``read() -> float`` returns ``_range_m`` with simulated noise
(e.g., multiply by a random factor between 0.95 and 1.05).
5. **``__str__``** returns ``"Sensor(): range=m"``.
6. In the ``if __name__ == "__main__"`` block:
.. code-block:: python
if __name__ == "__main__":
lidar = Sensor("lidar", 50.0)
print(lidar) # Sensor(lidar): range=50.0m
lidar.calibrate(2.5)
print(lidar) # Sensor(lidar): range=52.5m
lidar.calibrate(-5.0)
print(lidar) # Sensor(lidar): range=47.5m
**Expected output:**
.. code-block:: text
Sensor(lidar): range=50.0m
Sensor(lidar): range=52.5m
Sensor(lidar): range=47.5m
.. raw:: html
**Deliverables**
- ``lecture6/sensor_encapsulated.py``
- The program must run without errors and produce output matching the
expected format above.
.. dropdown:: Exercise 4 -- Robot with Sensor Suite
:icon: gear
:class-container: sd-border-primary
:class-title: sd-font-weight-bold
**Goal**
Combine classes, dunder methods, and encapsulation into a system where
a ``Robot`` manages a collection of ``Sensor`` objects. This exercise
ties together all concepts from the lecture.
.. raw:: html
**Specification**
Create a file ``lecture6/robot_with_sensors.py`` that implements the
following. Every class and method must have type hints and a
Google-style docstring.
1. **``Sensor`` class** with:
- Non-public attributes: ``_sensor_type``, ``_range_m``,
``_accuracy``
- Properties for ``range_m`` (validated: must be positive) and
``sensor_type`` (read-only)
- ``__str__`` and ``__repr__``
- ``__gt__`` comparing by ``range_m``
- ``__add__`` that returns a new Sensor with type ``"fused"``,
summed ranges, and averaged accuracies
2. **``Robot`` class** with:
- Non-public attributes: ``_name``, ``_battery``, ``_sensors``
(list)
- Properties for ``battery`` (validated 0--100) and ``name``
(read-only)
- ``add_sensor(sensor: Sensor) -> None`` method
- ``__contains__`` to check if a sensor type is in the robot
- ``__iter__`` to iterate over the robot's sensors
- ``__len__`` to get number of sensors
3. In the ``if __name__ == "__main__"`` block:
- Create a Robot, add multiple sensors.
- Iterate over sensors and print each one.
- Check membership (e.g., ``"lidar" in robot``).
- Fuse two sensors with ``+`` and print the result.
**Expected output:**
.. code-block:: text
=== Robot Sensors ===
Scout has 3 sensors:
Sensor(lidar): range=50.0m
Sensor(camera): range=30.0m
Sensor(ultrasonic): range=10.0m
=== Membership ===
Has lidar: True
Has radar: False
=== Sensor Fusion ===
Fused: Sensor(fused): range=80.0m
.. raw:: html
**Deliverables**
- ``lecture6/robot_with_sensors.py``
- The program must run without errors and produce output matching the
expected format above.
- All calculations must be computed dynamically (no hard-coded
results).
- Every class and method must include type hints and a Google-style
docstring.