ProgrammingBackend Python Developer

What are class property decorators (@property) in Python, how do they work, and why are they needed?

Pass interviews with Hintsage AI assistant

Answer.

The @property decorator allows you to turn a method into a "virtual" attribute of a class: it appears to the user of the class as a regular property but is controlled by the logic of a Python function. Initially, in Python, all instance attributes were directly accessible, but to support encapsulation, a mechanism was needed to manage access to data without changing the class interface.

History:

In early versions of Python, there was no explicit mechanism for restricting access to attributes. The task of encapsulation was achieved through conventions (for example, underlining), but any changes to the storage/validation logic for values would break compatibility. With the introduction of the @property decorator, it became possible to declare a method as an attribute, provide it with a getter, setter, and deleter, while keeping the previous class interface.

Problem:

When there is a need to subsequently add validation logic or compute a field's value, reconstructing the entire interface is too costly — it requires modifying all access points to the variable. At the same time, the visibility of the internal implementation of the field should not be exposed to the external consumer of the class. @property allows for easy "virtualization" of access.

Solution:

@property implements the "getter/setter with interface protection" pattern: you can add a computed property or control the assignment of a value without changing the client code.

Code example:

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 absolute zero!") self._celsius = value @property def fahrenheit(self): return self._celsius * 9 / 5 + 32 # Usage obj = Temperature(25) print(obj.celsius) # 25 print(obj.fahrenheit) # 77.0 obj.celsius = -300 # ValueError

Key features:

  • Allows the implementation of calculated, validated, or cached properties without changing the client interface
  • Provides a property interface (without parentheses when accessing)
  • Allows control over reading, writing, and deleting a value

Tricky questions.

Is it always possible to add a property to any class field without breaking backward compatibility?

No. If the attribute was public and used as a regular variable, replacing it with a property can be done transparently. But if somewhere the accesses went through __dict__ or type checks were performed via type(obj.attr), unexpected behavior may occur.

Can only a setter be declared without a getter for a property?

No, the getter (the method with @property) is obligatory at first; otherwise, Python does not know which property to "link" the setter to.

What is the order of decorators @property/@setter/@deleter and why?

@property is always written first (it creates a property object), followed by the method name — @<name>.setter and @<name>.deleter (they complement the previously created property).

class A: @property def value(self): ... @value.setter def value(self, v): ...

Common mistakes and anti-patterns

  • Direct access to a private attribute (for example, self._celsius) outside the class
  • Violating the principle of encapsulation: extra logic in getter/setter
  • Overriding the property in subclasses with name conflicts

Real-life example

Negative case

Class fields were public, then a computed property was added via property after the public API was released. In the old code, accessing the attribute implicitly changes behavior or causes an error.

Pros:

  • The user does not have to change access to the attribute

Cons:

  • Unexpected failures may arise in the code if access conditions become the logic of the setter/getter
  • Low compatibility if the client utilized direct access to __dict__

Positive case

Fields were defined as private (with one underscore) right from the design stage, and users worked only through methods/properties. Adding new logic to the property in the future occurred without changes to the client interface.

Pros:

  • Encapsulation
  • The ability to easily add validation and calculations

Cons:

  • Requires discipline in documentation and adherence to conventions; weak privacy (one underscore is not enough for complete protection)