How to mitigate spelling mistakes in Python properties?

77 Views Asked by At

I've read quite a bit about Python properties and I understand their benefits, as well as them being generally more Pythonic than getters/setters. However, I haven't found any mention of their usage being more prone to spelling mistakes leading to unexpected behavior for users, and I'd like to understand how this potential issue can be mitigated.

Consider the following example:

import datetime
import dateutil
class Person:
    def __init__(self, date_of_birth):
       self.date_of_birth = date_of_birth

    @property
    def date_of_birth(self):
        return self._date_of_birth

    @date_of_birth.setter
    def date_of_birth(self, value):
        # <Some checks about value>
        self._date_of_birth = datetime.datetime.strptime(value, '%d-%m-%Y').date()

    def compute_age(self):
        # Some computations involving today's date and self._date_of_birth
        return dateutil.relativedelta.relativedelta(datetime.date.today(), self.date_of_birth).years

# Compute age of someone
person = Person("15-06-1985")
person.compute_age() # Returns 38

# Update date of birth
person.date_of_birth = "17-08-2001"
person.compute_age() # Returns 22

# Update wrong attribute
person.date_of_brith = "25-11-1999" # No error or warning raised
person.compute_age() # Still returns 22

The last block of code is still perfectly valid, but person now has a useless date_of_brith attribute which is not used by person.compute_age(), while the intent of the developer was to update the actual date of birth of person. Since it generates no warning or error, this type of mistake could go undetected and yield unexpected results that are hard to debug in more complex applications. Using a setter method person.set_date_of_birth() instead of person.date_of_birth property would prevent such typos, since a typo in the method name would yield an exception upon execution. My question is therefore, doesn't that advocate for the usage of setter methods rather than properties? Otherwise, is there a clean way to mitigate this kind of spelling mistake when using properties?

1

There are 1 best solutions below

7
chepner On BEST ANSWER

It's a fundamental property of Python that arbitrary attributes can be added to an instance. (There are exceptions, but we need not concern ourselves with them here.)

To prevent such accidental creations, we can override __setattr__ to define what person.XXX = ... means. In this method, you can check that the attribute being assigned to is valid.

class Person:
    def __init__(self, date_of_birth):
       self.date_of_birth = date_of_birth

    def __setattr__(self, name, value):
        if name not in ["_date_of_birth"]:
            raise AttributeError(f"Cannot create attribute named '{name}'")
        super().__setattr__(name, value)

    ...

Note that we do not need to add date_of_birth to the white list, because no instance attribute named date_of_birth exists. When attempting to assign to person.date_of_birth, the default behavior is to call Person.date_of_birth.__set__ if it exists instead of creating the instance attribute.

That said, I would consider whether it is worth saddling your code with such a runtime check instead of making the user responsible for testing their code appropriately.