Learning

Property Decorators

Property Decorators

@property Decorator

python
class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): """Get the radius""" return self._radius c = Circle(5) print(c.radius) # 5 (no parentheses!) # c.radius = 10 # AttributeError! No setter defined

@setter Decorator

python
class Temperature: def __init__(self, celsius=0): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError('Below absolute zero!') self._celsius = value @property def fahrenheit(self): return self._celsius * 9/5 + 32 @fahrenheit.setter def fahrenheit(self, value): self.celsius = (value - 32) * 5/9 # Reuses celsius setter! t = Temperature(100) print(t.fahrenheit) # 212.0 t.fahrenheit = 32 # Uses fahrenheit setter print(t.celsius) # 0.0 # t.celsius = -300 # ValueError!

@deleter Decorator

python
class File: def __init__(self, name): self._name = name self._loaded = True @property def name(self): if not self._loaded: raise RuntimeError('File has been deleted') return self._name @name.deleter def name(self): print(f'Deleting {self._name}...') self._loaded = False f = File('data.txt') print(f.name) # 'data.txt' del f.name # 'Deleting data.txt...' # print(f.name) # RuntimeError!

Computed Properties

python
class Rectangle: def __init__(self, width, height): self.width = width self.height = height @property def area(self): return self.width * self.height @property def perimeter(self): return 2 * (self.width + self.height) @property def is_square(self): return self.width == self.height r = Rectangle(4, 5) print(r.area) # 20 (no parentheses!) print(r.is_square) # False

Read-Only Properties

python
class User: def __init__(self, first, last): self.first = first self.last = last @property def full_name(self): return f'{self.first} {self.last}' # No setter — read-only! u = User('John', 'Doe') print(u.full_name) # 'John Doe' # u.full_name = 'X' # AttributeError!
Key Rules
  • •@property turns a method into an attribute accessor — call it WITHOUT parentheses
  • •@setter must use the SAME name as the property it decorates: @property_name.setter
  • •Properties let you add validation LATER without changing the public API — backward compatible
  • •Computed properties recalculate every time they're accessed — cache if expensive
  • •Read-only properties have @property but NO @setter — assignment raises AttributeError
  • •Use @deleter for cleanup logic when `del obj.attr` is called
Your Task

Create a `Product` class with `__init__(self, name, price, discount_percent)`. Store price as `self._price` and discount as `self._discount`. Create: `@property price` (getter), `@price.setter` (validates price > 0), `@property discount` (getter), `@discount.setter` (validates 0-100), `@property final_price` (computed: price * (1 - discount/100), read-only). Demonstrate valid/invalid assignments and computed final_price.

EditorPython · JSX
PreviewUpdates on Run Tests
Loading preview…
Tests
Should define Product class with _price and _discount
Should have @property price
Should have @price.setter with validation
Should have @discount.setter validating 0-100
Should have @property final_price (computed)
Should demonstrate read-only error on final_price