The @property
decorator in Python is a powerful feature that allows you to define methods that behave like attributes.
This can significantly enhance the readability and maintainability of your code.
Let’s dive into what @property
is, how it works, and when to use it.
What is @property?
In Python, @property
is a built-in decorator that transforms a method into a “getter” for a read-only attribute with the same name.
It’s a pythonic way to use getters and setters without explicitly calling them as methods.
How does @property work?
The Python’s @property
decorator is syntactic sugar for creating descriptor objects.
Descriptors
are the underlying mechanism that control attribute access in Python,
defining special methods __get__
, __set__
, and __delete__
.
The @property
decorator creates a descriptor that wraps these methods,
allowing us to define computed attributes with fine-grained control over getting, setting, and deleting.
Code without the @property
decorator
1class Car:
2 def __init__(self, make, model, year):
3 self._make = make
4 self._model = model
5 self._year = year
6 self._mileage = 0
7
8 def get_make(self):
9 return self._make
10
11 def set_make(self, make):
12 self._make = make
13
14 def get_model(self):
15 return self._model
16
17 def set_model(self, model):
18 self._model = model
19
20 def get_year(self):
21 return self._year
22
23 def set_year(self, year):
24 self._year = year
25
26 def get_mileage(self):
27 return self._mileage
28
29 def set_mileage(self, mileage):
30 if mileage < self._mileage:
31 raise ValueError("Mileage can't be reduced")
32 self._mileage = mileage
33
34
35my_car = Car("Toyota", "Corolla", 2020)
36print(f"{my_car.get_year()=} {my_car.get_make()=} {my_car.get_model()=}")
37# my_car.get_year()=2020 my_car.get_make()='Toyota' my_car.get_model()='Corolla'
38my_car.set_mileage(5000)
39print(f"Current mileage: {my_car.get_mileage()}")
40# Current mileage: 5000
This code feels clunky and leas to more verbose code, especially if you have multiple attributes.
Code with the @property
decorator
1class Car:
2 def __init__(self, make, model, year):
3 self._make = make
4 self._model = model
5 self._year = year
6 self._mileage = 0
7
8 @property
9 def make(self):
10 return self._make
11
12 @property
13 def model(self):
14 return self._model
15
16 @property
17 def year(self):
18 return self._year
19
20 @property
21 def mileage(self):
22 return self._mileage
23
24 @mileage.setter
25 def mileage(self, value):
26 if value < self._mileage:
27 raise ValueError("Mileage can't be reduced")
28 self._mileage = value
29
30my_car = Car("Toyota", "Corolla", 2020)
31print(f"{my_car.year=} {my_car.make=} {my_car.model=}")
32# my_car.year=2020 my_car.make='Toyota' my_car.model='Corolla'
33my_car.mileage = 5000
34print(f"Current mileage: {my_car.mileage}")
35# Current mileage: 5000
Here in this example, the @property
decorator was used to create three attributes make
, model
and year
and defined as read-only attributes; moreover, mileage
was used with a setter and validation.
This code provides a clean interface to perform calculations or modifications when accessing these values.
Deletter, Setter, And Getter
- Getters: retrieve the value of an attribute, potentially performing calculations or applying logic in the process.
- Setters: allow for value assignment with optional validation or conversion.
- Deleters: less commonly used, provide a way to remove or reset a property, often performing cleanup tasks in the process.
Notes
The @property
decorator in Python is a powerful tool that allows us to create smart attributes.
As we’ve seen in these car-related examples, it can be used to:
- Create read-only attributes
- Implement validation when setting values
- Compute values on-the-fly
- Provide intuitive interfaces for different units of measurement
By using @property
, we can write more pythonic code that is both easy to use and maintain.
It allows us to encapsulate the internal workings of our classes while providing
a clean and intuitive interface to the outside world.