Skip to content

TASK: Add Measurement class (#18)#48

Open
Poorna-Chandra-D wants to merge 3 commits intoTheGittyPerson:mainfrom
Poorna-Chandra-D:feature/measurement-class
Open

TASK: Add Measurement class (#18)#48
Poorna-Chandra-D wants to merge 3 commits intoTheGittyPerson:mainfrom
Poorna-Chandra-D:feature/measurement-class

Conversation

@Poorna-Chandra-D
Copy link

@Poorna-Chandra-D Poorna-Chandra-D commented Mar 16, 2026

Summary

Adds a Measurement class to represent a numeric measurement with a unit (meters, centimeters, feet, inches), and integrates it into the Person class to replace the bare float height attribute.

Changes

  • New file src/measurement.py — adds Measurement class with:
    • value and unit attributes
    • Validation for non-negative values and recognized units (meters, centimeters, feet, inches)
    • __str__ returning a human-readable representation (1.8 meters)
    • __repr__ for developer convenience
  • Updated src/person.py:
    • height attribute type changed from float | None to Measurement | None
    • introduce() displays self.height naturally via str(Measurement) output
    • New set_height(value, unit) method for convenient height assignment.

Usage

from person import Person
from measurement import Measurement

p = Person(name="Alice", height=Measurement(1.7, "meters"))
p.set_height(170, "centimeters")
p.introduce()
# I am 170 centimeters tall.

Closes #18

@TheGittyPerson TheGittyPerson added enhancement New feature or request task This is a task issue / PR completing a task labels Mar 16, 2026
@Poorna-Chandra-D Poorna-Chandra-D force-pushed the feature/measurement-class branch from 0002fff to b6d0849 Compare March 16, 2026 03:02
@TheGittyPerson TheGittyPerson added the duplicate This issue or pull request already exists label Mar 16, 2026
@TheGittyPerson
Copy link
Owner

@Poorna-Chandra-D Since this is a duplicate of #46 (@P-r-e-m-i-u-m submitted the PR first but you started working first, I thought it would be unfair to choose one), are you interested in working with @P-r-e-m-i-u-m?

It's totally fine if you prefer to work solo (@P-r-e-m-i-u-m has already worked on many PRs so don't feel guilty). If you want to collaborate with him, you may discuss with him in his PR (or tag him here).

@TheGittyPerson TheGittyPerson force-pushed the feature/measurement-class branch from 171f367 to 1ef13e7 Compare March 17, 2026 16:32
@TheGittyPerson
Copy link
Owner

Updated your branch 👍

@TheGittyPerson TheGittyPerson force-pushed the feature/measurement-class branch from 1ef13e7 to da2cc7a Compare March 19, 2026 08:02
- Add src/measurement.py with Measurement class that stores a value and unit
- Measurement supports meters, centimeters, feet, and inches
- Updated Person.height to use Measurement type instead of float
- Added Person.set_height() method to set height with unit support
- Updated introduce() to display height using Measurement string representation

Closes TheGittyPerson#18

# Conflicts:
#	theperson/measurement.py
#	theperson/person.py

# Conflicts (2nd rebase conflict):
#	theperson/person.py
@TheGittyPerson TheGittyPerson force-pushed the feature/measurement-class branch from da2cc7a to beb8603 Compare March 22, 2026 12:06
Signed-off-by: Morpheus <167074500+thegittyperson@users.noreply.github.com>
Copy link
Owner

@TheGittyPerson TheGittyPerson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your PR. This is a very well-written module and there are minimal errors. Here are a few improvement suggestions I have for you:

TypeError: If value is not numeric or unit is not a string.
ValueError: If value is negative or unit is not recognized.
"""
if not isinstance(value, (int, float)):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicitly reject bool here as bool is a subclass of int and would pass this type check, which is unintended

Comment on lines +116 to +127
def __eq__(self, other: object) -> bool:
"""Compare measurements by their value in meters."""
if not isinstance(other, Measurement):
return NotImplemented
return isclose(self.to_meters(), other.to_meters(), rel_tol=0.0,
abs_tol=1e-9)

def __lt__(self, other: object) -> bool:
"""Compare whether one measurement is smaller than another."""
if not isinstance(other, Measurement):
return NotImplemented
return self.to_meters() < other.to_meters()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__eq__ is tolerant while __lt__ is not. Consider choosing one for consistency. I'd say have both use exact comparison.

Also, only __eq__ and __lt__ are implemented; operations like >= or <= will raise TypeError unless you add __le__, __gt__, __ge__ or use functools.total_ordering.

"""Return a human-readable string representation."""
if precision < 0:
raise ValueError(f"Precision must be non-negative, got {precision}")
rounded_value = round(self.value, precision)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

round() uses bankers rounding. for user-facing measurement strings you might want a specific rounding mode or formatting policy (or document it).

if normalized not in cls.UNIT_ALIASES:
raise ValueError(
f"Unit '{unit}' is not recognized. "
f"Valid units are: {', '.join(cls.VALID_UNITS)}"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cls.VALID_UNITS only includes canonical units (meters, centimeters, feet, ...) and not their shortened forms (m, cm, ft, ...).
Consider adding those to VALID_UNITS or mention something somewhere in the error message.

Comment on lines +233 to +253
def set_height(self, value: float, unit: str = "meters") -> None:
"""Set the person's height.

Args:
value: The numeric height value. Must be non-negative.
unit: The unit for the height. Defaults to 'meters'.
"""
self.physical.height = Measurement(value, unit)

def get_height_in(self, unit: str) -> Measurement:
"""Return the person's height converted into a different unit.

Args:
unit: The target unit for conversion.

Raises:
ValueError: If height has not been set.
"""
if self.physical.height is None:
raise ValueError("Height has not been set")
return self.physical.height.to_unit(unit)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like these methods don't belong here... Perhaps move them to the Physical data class above. Let me know what you think here.

@TheGittyPerson TheGittyPerson changed the title TASK: Add Measurement class (#18) TASK: Add Measurement class (#18) Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

duplicate This issue or pull request already exists enhancement New feature or request task This is a task issue / PR completing a task

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TASK: Add Measurement class

2 participants