๐Ÿ” Section 3: Add Properties + "Is Struggling?"

๐Ÿ“ Summary (What you will do)

In this section, you will make your Animal class easier (and safer) to use by adding:

  • Read-only properties so other parts of the program can access animal stats like pet.name and pet.hunger
  • A quick helper method called is_struggling() that checks if an animal's stats are getting too low (or hunger too high)

This is a key OOP idea: encapsulation. We keep the real data "protected" (the _name, _hunger, etc.), but we still allow safe access.


โœ… Checklist (You must complete these)

  • Open pet_manager.py
  • Find class Animal and locate the __init__ method
  • Add the @property getters directly after __init__
  • Add the is_struggling() method (right after the properties)
  • Do not remove your existing @abstractmethod special_action() method

โœ… No new constants/settings are added in this section, so you do not need to edit the top-of-file constants.


๐ŸŽ“ Core Concepts (New learning for this section)

1) What is a property?

A property lets you access a method like it's a normal variable.

Without properties (what we have right now), you might be tempted to do:

print(pet._hunger)

But _hunger is meant to be "private-ish." We want to protect it.

With a property, you can do:

print(pet.hunger)

That is cleaner and safer.

2) Why "read-only" is useful

In this project, we want other code (like the UI/dashboard) to be able to see stats, but not directly change them.

Read-only properties help because:

  • You prevent accidental changes like pet.hunger = 0 from outside the class
  • You keep all stat changes inside methods like feed(), play(), clean(), and tick()

Later, if we ever change how hunger is stored, the rest of the program can still use pet.hunger without breaking.

3) Boolean logic (or) in is_struggling()

is_struggling() returns a boolean:

  • True means: "This pet needs attention!"
  • False means: "This pet is doing okay."

We use or because struggling is true if ANY ONE of these is true:

  • hunger is too high (> 80)
  • happiness is too low (< 20)
  • cleanliness is too low (< 20)

๐Ÿ’ป Code to Write (Type this by hand in pet_manager.py)

Directions:

  1. Open pet_manager.py
  2. Find class Animal
  3. Inside class Animal, locate the __init__ method
  4. Right after __init__, type the following code by hand:

Code image: s03-code


๐Ÿง  Code Review & Key Concepts (What important lines do)

The @property decorator

@property
def hunger(self) -> int:
    return self._hunger

This creates a read-only property called hunger.

That means:

  • You can do: pet.hunger
  • You do not call it like a method: pet.hunger()

It reads your internal variable _hunger and returns it.

Read-only behavior

Because we did not write a setter, this should fail:

pet.hunger = 0

Python will raise an AttributeError because hunger is read-only.

is_struggling()

return self._hunger > 80 or self._happiness < 20 or self._cleanliness < 20

This is one line of boolean logic:

  • self._hunger > 80 checks if hunger is dangerously high
  • self._happiness < 20 checks if happiness is dangerously low
  • self._cleanliness < 20 checks if cleanliness is dangerously low

Using or means:

  • If any condition is true, the whole result becomes True

Later, the dashboard can use this to show a warning that a pet needs attention.


๐Ÿงช Test File: s3_test.py

โœ… Create this file

Create a new file in the same folder called:

s3_test.py

๐Ÿ’ป Code to write in s3_test.py

Code image: s03-test

๐Ÿง  What this test is doing (and how it works)

  • Animal is abstract, so the test creates a tiny subclass TestAnimal that implements special_action()
  • It prints out the property values to confirm the getters work
  • It tries pet.hunger = 0 to prove the property is read-only (it should raise AttributeError)
  • It manually sets _hunger, _happiness, and _cleanliness so we can test the is_struggling() logic on purpose

โœ… Run the test:

python s3_test.py

If your output matches the "Expected ..." comments, you completed Section 3 correctly.