ProgrammingBackend Developer

What are property decorators (@property) in Python, how do they help implement encapsulation, and what pitfalls are there in their usage?

Pass interviews with Hintsage AI assistant

Answer.

Background:

In classical OOP, encapsulation is achieved through private fields and getters/setters, which is cumbersome and not "Pythonic". In Python, starting from version 2.2, the @property decorator was introduced, allowing access to getter and setter methods as if they were regular attributes, thus implementing proper encapsulation with a convenient syntax.

Problem:

Without property decorators, one has to explicitly define methods for accessing and setting values (e.g., get_x() and set_x(val)), making the code less readable, and class users are not protected from direct access to internal data. This leads to issues during refactoring and changing the internal logic of storing or calculating values.

Solution:

The @property decorator allows defining getters, setters, and deleters with a single syntax. It looks concise, is convenient, encapsulates implementation details, and allows changing the method of calculating a property without breaking the class interface.

Example code:

class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("Temperature below -273.15°C is not possible!") self._celsius = value

Key Features:

  • The @property decorator allows accessing methods as if they were regular attributes.
  • Easily add validation or caching logic.
  • Change logic without changing the interface — class users do not notice changes.

Tricky Questions.

Can a property be read-only, but not writable or deletable?

Yes, if you define only a method with the @property decorator without a setter and deleter, the property will be read-only.

class Sample: @property def value(self): return 42

What happens if the property name coincides with a private attribute?

Usually, property is used as a "layer" to a private attribute, whose name starts with an underscore (e.g., _x). Such coincidences should be avoided, otherwise you will face recursive calls:

class Bad: @property def x(self): return self.x # Infinite recursion

Can a property be assigned only to the class?

No, the standard @property works with class instances. To create a class property, you should use third-party patterns or special libraries (@classmethod together with property does not work directly).

Common Mistakes and Anti-Patterns

  • Incorrect naming of attributes (e.g., duplicating the name of the property and internal field).
  • Incorrectly implemented setter leads to recursive calls.
  • Misuse of property for properties that do not require calculation/validation.

Real-life Example

Negative Case

A developer accesses a class field directly (self.celsius) instead of through the property. Later, validation is added, but class consumers can still directly modify the private attribute and bypass checks.

Pros:

Easy and quick to work with until the logic gets complicated.

Cons:

Encapsulation is violated, making it easy to achieve an incorrect state of the object, leading to confusion.

Positive Case

Using property and hiding the internal attribute through _celsius. Validation, caching, and logic are centralized within the property.

Pros:

The code is protected, the interface is stable — the property implementation can change without affecting class users.

Cons:

With large and complex objects, debugging complexity can increase if overused with properties.