SoFunction
Updated on 2024-11-07

Introducing Python's @property decorator usage

When binding attributes, if we expose the attributes directly, although it's easy to write, there's no way to check the parameters, resulting in being able to change the scores to whatever you want:

s = Student()
 = 9999

This is clearly illogical. In order to limit the scope of the score, you can set the score via a set_score() method, and then get the score via a get_score(), so that the parameters can be checked in the set_score() method:

class Student(object):

  def get_score(self):
    return self._score

  def set_score(self, value):
    if not isinstance(value, int):
      raise ValueError('score must be an integer!')
    if value < 0 or value > 100:
      raise ValueError('score must between 0 ~ 100!')
    self._score = value

Now, operating on any Student instance will not allow you to set the score as you wish:

>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
 ...
ValueError: score must between 0 ~ 100!

However, the above invocation method is again slightly more complicated and not as straightforward and simple as using attributes directly.

Is there a way to both check parameters and access variables of a class in a simple way like properties? For the perfectionist Python programmer, this is a must!

Remember how decorators can dynamically add functionality to functions? Decorators work just as well for class methods, and Python's built-in @property decorator is responsible for turning a method into a property call:

class Student(object):

  @property
  def score(self):
    return self._score

  @
  def score(self, value):
    if not isinstance(value, int):
      raise ValueError('score must be an integer!')
    if value < 0 or value > 100:
      raise ValueError('score must between 0 ~ 100!')
    self._score = value

The implementation of @property is a bit more complex, so let's examine how it is used. Turning a getter method into a property is as simple as adding @property, at which point @property itself creates another decorator @ that is responsible for turning a setter method into a property assignment, and so we have a controlled property operation:

>>> s = Student()
>>>  = 60 # OK, actually translates to s.set_score(60)
>>>  # OK, actually translates to s.get_score()
60
>>>  = 9999
Traceback (most recent call last):
 ...
ValueError: score must between 0 ~ 100!

Noticing this magic @property, we know when we manipulate the instance property that the property is most likely not exposed directly, but through getter and setter methods.

It is also possible to define read-only properties, where defining only the getter method and not the setter method is a read-only property:

class Student(object):

  @property
  def birth(self):
    return self._birth

  @
  def birth(self, value):
    self._birth = value

  @property
  def age(self):
    return 2014 - self._birth

The above birth is a read-write attribute, while age is a read-only attribute, because age can be calculated based on birth and the current time.
wrap-up

The widespread use of @property in class definitions allows callers to write short code while ensuring that the necessary checks are made on parameters, so that there is less chance of errors when the program is run.