I have spent over a decade building large-scale Python applications, and if there is one tool that separates clean, professional code from “just getting it done,” it is the @property decorator.
When I first started, I used to write Java-style getters and setters for everything, but Python offers a much more elegant way to handle data.
In this tutorial, I’ll show you exactly how to use the @property decorator to make your classes more “Pythonic” and robust.
What is the @property Decorator?
The @property decorator is a built-in feature in Python that allows you to define a method but access it like an attribute.
It is essentially a way to use getters and setters without changing the user interface of your class.
Method 1: Use @property for Basic Getters
The most common use case I encounter is wanting to protect an attribute or calculate a value on the fly. Instead of exposing a raw variable, you wrap it in a property.
Suppose we are building a payroll system for a company in New York. We want to ensure the employee ID is accessible but not directly modified by mistake.
class US_Employee:
def __init__(self, emp_id, name):
self._emp_id = emp_id # Internal attribute
self.name = name
@property
def emp_id(self):
"""The getter for employee ID."""
return f"EMP-NY-{self._emp_id}"
# Usage
employee = US_Employee(1024, "John Doe")
print(employee.emp_id) # Output: EMP-NY-1024I executed the above example code and added the screenshot below.

In my experience, using this approach from the start saves you from huge refactoring headaches later when your logic needs to change.
Method 2: Add Validation with @property.setter
One of the biggest mistakes I see junior developers make is allowing invalid data to enter an object. With the .setter decorator, you can intercept the assignment and validate it.
Let’s look at a real estate example where we handle property taxes in Texas. We need to ensure the appraised value is never a negative number.
class TexasProperty:
def __init__(self, address, value):
self.address = address
self.value = value # This calls the setter automatically
@property
def value(self):
return self._value
@value.setter
def value(self, market_price):
if market_price < 0:
raise ValueError("The appraised value in Texas cannot be negative.")
print(f"Setting value to: ${market_price:,}")
self._value = market_price
# Usage
home = TexasProperty("123 Lone Star Lane, Austin", 450000)
home.value = 500000 # Updates correctly
# home.value = -100 # This would raise a ValueErrorI executed the above example code and added the screenshot below.

I find this incredibly useful because it keeps your data integrity logic right where it belongs, inside the class definition.
Method 3: Use @property for Computed Attributes
Sometimes you don’t want to store a piece of data because it depends on other attributes.
In the USA, sales tax varies by state. We can use a property to calculate the total price including tax dynamically.
class CaliforniaOrder:
def __init__(self, item_name, price):
self.item_name = item_name
self.price = price
self.tax_rate = 0.0725 # CA Base Sales Tax
@property
def total_price(self):
"""Calculates total price on the fly."""
return round(self.price * (1 + self.tax_rate), 2)
# Usage
order = CaliforniaOrder("MacBook Pro", 2000)
print(f"Total price in CA: ${order.total_price}")I executed the above example code and added the screenshot below.

This prevents the “stale data” bug, where you update the price but forget to update the total.
Method 4: Clean Up with @property.deleter
The deleter is the least used part of the property decorator, but it’s vital when you need to perform cleanup.
For instance, if you are managing a cloud server instance in an AWS US-East region, you might want to log a message or clear a cache when an attribute is deleted.
class CloudServer:
def __init__(self, instance_id):
self._instance_id = instance_id
@property
def instance_id(self):
return self._instance_id
@instance_id.deleter
def instance_id(self):
print(f"Shutting down and clearing records for instance: {self._instance_id}")
del self._instance_id
# Usage
aws_node = CloudServer("i-04f5e6a7b8c9d")
del aws_node.instance_idI rarely use deleters in simple scripts, but in production-grade systems, they are great for resource management.
Why You Should Use @property Instead of Regular Methods
You might ask, “Why not just use get_value() and set_value()?”
In Python, we value readability. Using @property allows you to change your implementation without changing the API.
If you start with a simple attribute like self.price = 100 and later realize you need validation, you can switch to a @property without breaking any code that already uses obj.price.
This is what we call “graceful evolution” of code.
Common Issues to Avoid
Even with experience, I still see people trip up on these:
- Infinite Recursion: If your property name is the same as the internal variable (e.g., self.price = price inside the setter of price), Python will call the setter again and again until it crashes. Always use a leading underscore for the internal variable (e.g., self._price).
- Overusing Properties: Don’t turn every single method into a property. If a calculation is computationally expensive (like a database query), it should probably be a method so the user knows it has a cost.
Final Thoughts on Python Properties
Mastering the @property decorator was a turning point in my career. It allowed me to write classes that feel native to Python while maintaining strict control over the data.
You may also like to read:
- How to Use Private Variables in Python for Data Hiding
- How to Use Python slots to Optimize Memory Performance
- Data Encapsulation in Python
- Python @property vs Normal Methods

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.