@property

RoadmapsPython Backend

Overview

The `@property` decorator transforms a class method so it can be accessed like a normal attribute, evaluating its logic on-the-fly. If you have an `User` object, you can compute `user.full_name` dynamically rather than storing it. You can also define `.setter` and `.deleter` methods under the same property name to intercept assignment (`user.full_name = 'Alice Smith'`) or deletion. The mental model: variables are usually direct labels to objects, but a property is a 'computed label' that secretly runs a function whenever you read, write, or delete it. Analogy: a property is like an automatic sliding door—it looks like a regular wall (an attribute), but approaching it triggers a hidden motor (a function call).

It allows you to safely change internal implementation without breaking the external API. You can start with a simple attribute, and later add validation or computation without forcing everyone to rewrite `obj.attr` to `obj.get_attr()`.

Where used: SQLAlchemy models, Pydantic computed fields, Django models

Why learn this

Code walkthrough

class User:
    def __init__(self, age):
        self._age = age

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError('Age cannot be negative')
        self._age = value

u = User(20)
u.age = 25
print(u.age)

Focus: u.age = 25

Aha moment

class BankAccount:
    def __init__(self, balance):
        self._balance = balance

    @property
    def balance(self):
        return self._balance

    @balance.setter
    def balance(self, amount):
        self._balance = amount

acc = BankAccount(100)
acc.balance += 50
print(acc.balance)

Prediction: What does this print?

Common guess: It raises an error because you can't use += on a property.

The `+=` operator automatically expands to `acc.balance = acc.balance + 50`, effectively calling both the getter to read the current balance and the setter to write the new balance.

Common mistakes

Glossary

evaluating
Running a piece of code or calculation to determine its final result or value. Example: `result = 2 + 2`.
sprawl
When code becomes messy, spread out, and difficult to manage because too many similar or repetitive methods are used. Example: `obj.get_name()`.

Recall questions

Understanding checks

What happens when you run this code?

It raises an AttributeError.

The `env` property only has a getter defined. Attempting to assign to a property without a corresponding `@env.setter` raises an `AttributeError`.

Why would you choose to use a `@property` instead of leaving `age` as a simple public integer attribute on a `User` class?

To allow future validation or computation without breaking the external API.

Using a property lets external code keep using `user.age`, but allows you to add a hidden function to intercept assignments (e.g., preventing negative values) without forcing everyone to change to `user.set_age(...)`.

Practice tasks

Implement a Read-Only Computed Property

Modify the starter code for the `Rectangle` class to add an `@property` called `area` that computes and returns `self.width * self.height` on-the-fly. Do not provide a setter.

Challenge

Validating Password Length

Create a `UserAccount` class that initializes with a `username` and a `password`. Use a property for `password` to ensure it is always stored in `self._password`. The setter should raise a `ValueError` with the message 'Password too short' if the new password is less than 8 characters long. The constructor should also validate the initial password using this setter.

Continue learning

Previous: Decorators

Return to Python Backend Roadmap