Properties: Controlled Attributes
About 18 minutes
Why this matters
Attributes are convenient, but plain ones cannot protect themselves: anyone can set age = -5. A property looks like an attribute from the outside (obj.age) while running code underneath, so you can validate, compute values on demand, or make something read-only, without changing how other code uses the class.
The idea
class Person:
def __init__(self, age):
self.age = age # goes through the setter below
@property # the "getter": runs on p.age
def age(self):
return self._age
@age.setter # runs on p.age = 30
def age(self, value):
if value < 0:
raise ValueError("age cannot be negative")
self._age = value
@property # no setter: read-only
def is_adult(self):
return self._age >= 18
- The real value hides in
self._age. A leading underscore means “internal, please do not touch”. - Assigning in
__init__throughself.age = agemeans even the first value is validated. - A property with no setter is read-only: assigning raises
AttributeError. - Properties computed from other attributes never go out of date.
Try it
Create a Person, set p.age = -3 and read the ValueError. Then try p.is_adult = True.
Your turn
Write two classes:
Temperature(celsius)with properties:celsius: a normal readable and writable value that raisesValueErrormentioningabsolute zerofor anything below-273.15(also when creating the object).fahrenheit: readable and writable; setting it changes the celsius value (and is validated too).kelvin: read-only, celsius plus273.15.
Rectangle(width, height)with ordinarywidthandheightattributes and read-only computed propertiesarea,perimeterandis_square.
Recap
@propertymakes a method behave like an attribute;@name.setterhandles assignment.- Use setters to validate and getters to compute.
- No setter means read-only.
Common mistakes
- Setting
self.celsiusinside thecelsiussetter, which calls the setter again forever (RecursionError). Store inself._celsius. - Calling a property with parentheses (
t.kelvin()):TypeError: 'float' object is not callable. - Putting the
@propertymethod after the setter, so the setter has no property to attach to.