Filter concepts by levelShowing all levels.

Python · Properties

Properties

Reading and writing an attribute-style value that actually runs a method underneath

  • @propertyobj.name, no parentheses — a method read like an attribute
  • @name.settermakes obj.name = value run real code, most often validation
  • @name.deleterdefines what del obj.name does — often a reset, not a true removal
  • Computed propertiesderived from other attributes on every read — cannot go stale

1.@property

@property turns a method into something read like a plain attribute — obj.value instead of obj.value(). The method still runs every time the attribute is read, so it can compute, validate, or look something up on the fly.

Key features

  • @property goes directly above a method that takes only self
  • Callers read it as obj.name, with no parentheses — it looks like a plain attribute
  • The method runs fresh on every read — nothing is cached automatically
  • Converting a plain attribute to a property later does not break existing callers
  • A property with no setter is read-only — assigning to it raises AttributeError

Plain attribute vs. @property, from the caller's side

Plain attribute vs. @property, from the caller's side
Caller writesWhat actually happens
obj.name (plain attribute)reads a stored value directly, no code runs
obj.name (with @property)calls a method behind the scenes, returns its result
obj.name = x (no setter defined)AttributeError — read-only property
obj.name() wrong — a property is read without parentheses
Reading a property runs a method, but looks like an attribute

obj.area

no parentheses, looks like an attribute

@property method runs

computes the value fresh, every read

returns a plain value

caller never sees the method call

  1. obj.area — no parentheses, looks like an attribute
  2. @property method runs — computes the value fresh, every read
  3. returns a plain value — caller never sees the method call

Command

python
class Name:
    @property
    def value(self):
        return self._value      # computed, validated, or looked up

@property makes a method readable as obj.name, no parentheses — it reruns on every read and is read-only unless a setter is added.

2.Property setters

@name.setter defines what obj.name = value actually does — most often validating the incoming value before storing it. Without a setter, a @property is read-only; adding one makes assignment run real code instead of failing.

Key features

  • @name.setter must reuse the SAME method name as the @property it pairs with
  • The setter method takes self and exactly one value parameter
  • Common use: validate the incoming value, then store it in a differently-named attribute
  • A setter can transform the value before storing it, not just accept or reject
  • Defining a setter is what turns a property from read-only into read-write

Getter alone vs. getter + setter

Getter alone vs. getter + setter
DefinitionWhat obj.name = value does
Only @property (getter)AttributeError — no setter defined
@property + @name.setterruns the setter method with value as the argument
Setter raises inside its bodythe raised exception propagates — assignment never completes
Setter stores under a different name (self._name)avoids infinite recursion into the property itself
t.celsius = -300 — the setter runs before storage
FalseTrue

t.celsius = -300

@celsius.setter runs

value = -300

value < -273.15?

self._celsius = value

ValueError raised

_celsius left untouched

  • t.celsius = -300
    • leads to @celsius.setter runs
  • @celsius.setter runs — value = -300
    • leads to value < -273.15?
  • value < -273.15?
    • leads to self._celsius = value (False)
    • on error, leads to ValueError raised (True)
  • self._celsius = value
  • ValueError raised — _celsius left untouched

Command

python
class Name:
    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, new_value):
        self._value = new_value    # validate/transform here

@name.setter must reuse the same method name as its @property — a mismatched name never attaches, and self.name inside it recurses forever.

3.Property deleters

@name.deleter defines what del obj.name actually does — the least common of the three property methods, used for cleanup, resetting to a default, or blocking deletion outright by raising inside it.

Key features

  • @name.deleter must reuse the same method name as its @property, like the setter does
  • del obj.name calls the deleter instead of the getter or setter
  • Without a deleter, del obj.name raises AttributeError on a property
  • A deleter commonly resets the underlying attribute rather than truly removing it
  • A deleter can also just raise, to explicitly forbid deletion with a clear message

What del obj.name does, by definition

What del obj.name does, by definition
Definitiondel obj.name behaviour
@property only, no deleterAttributeError — cannot delete
@property + @name.deleterruns the deleter method
Deleter does del self._namegenuinely removes the underlying storage
Deleter raisesblocks deletion with a custom, explicit error

Command

python
class Name:
    @property
    def value(self):
        return self._value

    @value.deleter
    def value(self):
        del self._value          # or reset, or raise to forbid it

@name.deleter defines what del obj.name does — often a reset rather than a true removal. Without one, deleting a property raises AttributeError.

4.Computed properties

A computed property derives its value from other attributes every time it is read, instead of being stored directly — like full_name computed from first_name and last_name. It can never go stale, because there is nothing stored to go stale.

Key features

  • A computed property derives its value from other attributes, not from its own stored state
  • It stays correct automatically — there is no separate copy that can drift out of sync
  • The trade-off is cost: it recomputes on every single read, with no caching by default
  • Caching a computed property (lazy, first-read) is opt-in — see property deleters
  • A computed property can still validate or transform, exactly like any property getter

Stored attribute vs. computed property, for the same information

Stored attribute vs. computed property, for the same information
ApproachWhat can go wrong
self.full_name = f"{first} {last}" (stored once)stale if first_name/last_name change afterward
@property def full_name(self): (computed)always correct, but recomputed on every read
Cached computed property (lazy, reset on write)correct AND cheap, but adds real bookkeeping
Recomputing something expensive on every readcorrect but potentially slow — measure before caching
A computed property never stores its own answer

first_name, last_name

the real, stored state

full_name property

recomputes from them on every read

always in sync

nothing separate to go stale

  1. first_name, last_name — the real, stored state
  2. full_name property — recomputes from them on every read
  3. always in sync — nothing separate to go stale

Command

python
class Person:
    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"   # derived, not stored

A computed property derives its value on every read, so it can never go stale — only the cost of recomputing is the trade-off.

Remember

  • @property makes a method readable as obj.name — no parentheses, reruns on every read, read-only unless a setter is added.
  • @name.setter must reuse the exact same method name as its @property, and store under a different name to avoid recursion.
  • Without a deleter, del obj.name raises AttributeError — a deleter is the least commonly needed of the three property methods.
  • A computed property derives its value from other attributes, so it can never go stale — the cost is recomputing on every read.

Pro tip

  • A setter assigning to self.name (not self._name) inside its own definition recurses infinitely — always store under a different name.
  • A deleter does not have to remove anything — resetting to a default (forcing a lazy recompute) is a common, valid use.
  • Converting a plain attribute to a computed @property later never breaks existing callers — obj.name still reads the same either way.
  • Cache a computed property only after measuring it is actually expensive — caching reintroduces the staleness risk it was chosen to avoid.

Next up

  • Dunder methods
  • Advanced OOP

Sources

  • property — docs.python.org/3/library/functions.html#property
  • Every example executed on Python 3.14.3 · checked 2026-08-21

Spotted a mistake on this page? Tell us.