Properties
Start classes with plain attributes. When a value later needs checking or computing, @property turns the attribute into method calls without changing how callers read or assign it.
class Circle:
def __init__(self, radius):
self.radius = radius # setter runs
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("negative")
self._radius = value
@property
def area(self):
return 3.14159 * self.radius ** 2
c = Circle(2)
c.radius = 3
print(c.radius, round(c.area, 2))- The decorated method is the getter, and
@radius.setteradds assignment under the same name. - The real value lives under another name, by convention
_radius. Assigningself.radiusinside the setter would call the setter again, forever. - A property without a setter is read-only, so assigning to
c.arearaisesAttributeError. - Assigning in
__init__goes through the setter, so construction is validated too.
Keep getters cheap and free of side effects. Callers expect reading an attribute to be quick and harmless.
Your task
Write Temperature(celsius) where celsius is a property whose setter raises ValueError below -273.15, including in __init__. Add a fahrenheit property with a setter that stores the matching Celsius value, and a read-only kelvin property.
The explanation opens here once every check passes.