Properties
Use @property to expose methods like attributes — computed values and validated setters, without changing how callers use your object.
Sometimes an attribute should be computed on the fly, or a value should be validated before it's stored. Python's @property lets a method be accessed like a plain attribute — no parentheses — so you can add logic later without breaking callers.
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius ** 2
c = Circle(10)
print(c.area) # accessed like an attribute, no ()
c.radius = 20
print(c.area) # recomputed automatically#Validated setters
class Account:
def __init__(self, balance):
self._balance = balance
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("balance cannot be negative")
self._balance = value
a = Account(100)
a.balance = 50 # goes through the setter
print(a.balance) # 50Start simple, upgrade later
Begin with a plain attribute (self.radius = radius). If you later need validation or computation, turn it into a @property — callers still write obj.radius, so nothing else changes. This is why Python doesn't need Java-style getters/setters up front.
How do you access a value defined with @property?
Key takeaways
- `@property` turns a method into a read-only attribute accessed without ().
- Add a matching `@<name>.setter` to validate or transform values on assignment.
- Store the backing value in a `_name` attribute by convention.
- You can convert a plain attribute to a property later without changing calling code.
What does this print?
class Box:
def __init__(self, w, h):
self.w, self.h = w, h
@property
def area(self):
return self.w * self.h
b = Box(3, 4)
print(b.area)Calling `c.area()` fails with 'float object is not callable'. Why?
class C:
def __init__(self, r): self.r = r
@property
def area(self): return 3.14 * self.r ** 2
c = C(2)
print(c.area())Complete the decorator that validates the value when `name` is assigned.
@name. def name(self, value): self._name = value.strip()
Give a Temperature class (storing self.celsius) a read-only @property called fahrenheit that returns celsius * 9/5 + 32.
Try it live — edit the code and hit Run to execute real Python: