← Back to Python

@property — Getter, Setter, Deleter

PythonMidpython

The Question

How does @property work as a getter, setter, and deleter?

What a Strong Answer Covers

  • @name.setter syntax with self
  • "underscore backing attribute
  • "no return in setter

Senior-Level Answer

Python's `@property` decorator turns a method into a descriptor that's accessed like an attribute — `obj.name` instead of `obj.name()`. This lets you expose a clean attribute interface while executing code (validation, computation, lazy loading) on access or assignment.

The getter is defined with `@property`. It runs when the attribute is read: `obj.temperature`. If you only define a getter, the attribute is read-only — attempting to assign raises `AttributeError: can't set attribute`.

The setter is defined with `@temperature.setter` (using the property's name). It runs when the attribute is assigned: `obj.temperature = 37`. The setter receives the new value as an argument. This is where you typically validate: raise `ValueError` for out-of-range values, coerce types, or trigger side effects like invalidating a cache.

The deleter is defined with `@temperature.deleter`. It runs when `del obj.temperature` is executed. Less commonly needed, but useful for releasing resources, clearing cached values, or resetting to a default state.

The full pattern in practice: ```python class Circle: def __init__(self, radius): self._radius = radius # underscore: internal storage

@property def radius(self): return self._radius

@radius.setter def radius(self, value): if value < 0: raise ValueError("Radius must be non-negative") self._radius = value

@radius.deleter def radius(self): del self._radius ```

A critical point: the property and its setter/deleter must share the same name. The decorator syntax `@radius.setter` works because `@property` returns a property object with `.setter()`, `.getter()`, and `.deleter()` methods that return new property objects with the respective accessor replaced.

The key design benefit: you can start with a plain attribute, then refactor to a property with validation later — all callers continue to use `obj.radius` unchanged. This is why Pythonistas don't write getters and setters upfront like in Java — you pay the property cost only when you need it.

Properties are implemented as descriptors, which is the broader protocol Python uses for attribute access control (`__get__`, `__set__`, `__delete__`). `property` is a built-in descriptor class.

What Separates a 2/3 from a 3/3

2/3 — Passing but Incomplete

Correctly explains getter, setter, and deleter syntax and when each runs, explains the private attribute convention (_name), and articulates the design benefit (uniform attribute interface with controlled access).

3/3 — Strong Answer

All of the above plus: explains why properties allow deferring validation to when it's needed (start with plain attribute, refactor later), mentions the descriptor protocol underneath, and explains the mechanism by which @radius.setter works (property object's .setter() method).

Common Mistakes

  • Defining setter or deleter without using the property's name — e.g., @property def name, then @name_setter instead of @name.setter. This is a common syntax mistake.
  • Using the same name for the property and the backing attribute (self.radius = value inside the setter calls the setter recursively, causing infinite recursion).
  • Not explaining the design rationale — describing syntax without explaining why you'd prefer property over a plain getter/setter method pair.
  • Thinking properties are required for encapsulation in Python — they're an optional addition; plain attributes are acceptable until validation or computation is needed.

Follow-Up Questions

  • Why does self.radius = value inside a setter cause infinite recursion, and how do you fix it? — self.radius triggers the setter again. Fix by storing to self._radius (underscore-prefixed backing attribute) instead.
  • What is a descriptor protocol and how does property use it? — Descriptors implement __get__, __set__, __delete__. property is a built-in descriptor class — @property creates a descriptor object that Python invokes on attribute access.
  • When would you use __slots__ alongside a property? — __slots__ restricts instance attributes and can improve memory. If using __slots__, include the backing attribute name (e.g., '_radius') in __slots__ alongside the property definition.
  • Can a property be defined on a class without using the decorator syntax? — Yes: radius = property(fget=get_radius, fset=set_radius, fdel=del_radius). Decorator syntax is just syntactic sugar for this.

Related Questions

  • GIL — What It Is and What It Protects
  • ThreadPoolExecutor & ProcessPoolExecutor
  • Decorators — Under the Hood
  • Generators & yield
  • Context Managers

Can You Explain This Cold?

Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.

Get Graded — Free Assessment
GrindQuestionsAITechnical interview assessment
TermsPrivacyAbout