Property Decorator
Property Decorator
Section titled βProperty DecoratorβWhat it is
Section titled βWhat it isβ@property turns a method into something accessed like a plain attribute (no parentheses), letting you run code β validation, computation, logging β on βgetβ (and optionally βsetβ or βdeleteβ) while callers keep writing obj.value instead of obj.get_value().
Before this feature
Section titled βBefore this featureβJava/C#-style manual getters and setters:
class Circle: def __init__(self, radius): self._radius = radius
def get_radius(self): return self._radius
def set_radius(self, value): if value <= 0: raise ValueError("radius must be positive") self._radius = value
c = Circle(5)c.set_radius(10) # verbose, and every caller must remember to use itprint(c.get_radius())After this feature
Section titled βAfter this featureβclass Circle: def __init__(self, radius): self._radius = radius
@property def radius(self): return self._radius
@radius.setter def radius(self, value): if value <= 0: raise ValueError("radius must be positive") self._radius = value
@property def area(self): # read-only computed property β no setter return 3.14159 * self._radius ** 2
c = Circle(5)c.radius = 10 # looks like plain attribute access, runs the validatorprint(c.radius) # 10print(c.area) # 314.159 -- computed on every access, not storedWhy this is better
Section titled βWhy this is betterβ- Same syntax for simple and computed attributes: you can start with a plain attribute and add validation/computation later via
@propertywithout breaking every caller that wroteobj.value. - Read-only by default: a
@propertywith no matching@x.setteris get-only β assignment raisesAttributeError, a clean way to expose computed or derived values. - Encapsulation without ceremony: no
get_x()/set_x()pairs cluttering the public API.
Key notes / edge cases
Section titled βKey notes / edge casesβ- The getter, setter, and deleter must share the same method name β
@radius.setterrefers back to theradiusproperty defined just above it. - Properties are computed every time theyβre accessed unless you cache the result yourself β
c.arearecalculates on every read, it isnβt stored. @propertyis implemented via the descriptor protocol (__get__/__set__on the class) β the same mechanism that makes methods themselves work.- Assigning to a property with no setter defined raises
AttributeError: can't set attribute, not a silent no-op.
class ReadOnly: @property def value(self): return 42
r = ReadOnly()r.value = 10 # AttributeError: property 'value' of 'ReadOnly' object has no setterQuick practice
Section titled βQuick practiceβ-
What happens if you assign to a
@propertythat has no@x.setter?Answer
`AttributeError` β a getter-only property is read-only by default. -
Is a
@propertyβs computed value cached automatically?Answer
No β the getter method runs fresh every time the property is accessed, unless you add your own caching. -
What underlying mechanism makes
@propertywork?Answer
The descriptor protocol β `property` is a descriptor implementing `__get__`, `__set__`, and `__delete__` on the class.