Python24 min read
Python Properties
Use @property to create clean getter/setter logic with validation, computed attributes, and read-only fields, without breaking your public API.
David Miller
August 25, 2025
3.8k137
Properties let you access methods like attributes.
Instead of:
- person.get_name()
- person.set_name("Sarah")
You can write:
- person.name
- person.name = "Sarah"
But still run validation behind the scenes.
## Why properties matter
They help you:
- validate inputs
- create read-only attributes
- compute values dynamically
- keep your API clean
## Basic property (getter + setter)
```python
class Person:
def __init__(self, name):
self._name = name # underscore = internal
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value or not value.strip():
raise ValueError("Name cannot be empty")
self._name = value.strip()
p = Person("Tom")
print(p.name)
p.name = " Sarah "
print(p.name)
```
Expected output:
```
Tom
Sarah
```
## Read-only property
```python
import math
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return math.pi * (self.radius ** 2)
c = Circle(5)
print(round(c.area, 2))
```
Here, `area` has no setter, so it cannot be directly changed.
## Computed property with two-way conversion
```python
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@property
def fahrenheit(self):
return (self._celsius * 9/5) + 32
@fahrenheit.setter
def fahrenheit(self, value):
self._celsius = (value - 32) * 5/9
t = Temperature(25)
print(t.fahrenheit)
t.fahrenheit = 86
print(round(t.celsius, 2))
```
Expected output:
```
77.0
30.0
```
## Real-world example: bank account validation
```python
class BankAccount:
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
acc = BankAccount(1000)
acc.balance = 1200
print(acc.balance)
# acc.balance = -10 # would raise error
```
## Graph: property access
```mermaid
flowchart LR
A[Read: obj.name] --> B[@property getter]
C[Write: obj.name = value] --> D[@name.setter]
B --> E[Return data]
D --> F[Validate + store]
```
## Key points
- Properties look like attributes, behave like methods
- Great for validation and computed values
- Keeps public API stable even if internals change
#Python#Advanced#OOP