All posts

Should I use Python properties?

Python properties have been surprisingly divisive amongst developers I've worked with.

In simple use cases, they're great.

class User():
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def full_name(self):
        return f'{self.first_name} {self.last_name}'


>>> User('Shirley', 'Jones').full_name
Shirley Jones

The problem is they can cause confusion.

class User():
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def get_full_name(self):
        return f'{self.first_name} {self.last_name}'


# Feels weird:
>>> User('Shirley', 'Jones').get_full_name
Shirley Jones

Just by changing the method name, it feels unnatural for this to be a property. Calling my_user.get_full_name feels like it should have brackets after it, because it sounds like a function. So naming is definitely important when using properties.

Also, properties work great if you're confident you won't need to add any arguments in the future.

Imagine we wanted to modify get_full_name so it had an include_title argument.

If we implemented it as a property, we'll break everyone's code, because now it'll have to be called as a function to work properly:

class User():
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def get_full_name(self, include_title=False):
        fullname = f'{self.first_name} {self.last_name}'
        if include_title:
            fullname = f'Madam {fullname}'

        return fullname


# We broke our existing code:
>>> User('Shirley', 'Jones').get_full_name
Error!

This might not matter much in small projects, but if you're a library author you don't want to introduce a breaking change just by adding an argument to a property.

In API design, properties can be overused too. If you're designing a fluent interface, you don't want to add a cognitive load to a programmer by making them consider 'is this a property or a method?'.

Take this example:

class Select(Query):

    def where(self, query) -> Select:
        # do stuff
        return self

    @property
    def first(self) -> Select:
        # do stuff
        return self

    def run(self):
        return 'some data'

To use this API:

select = Select().where(some_query).first.run()

Rather than having to remember that first is a property, it's cleaner to have them all as plain methods.

select = Select().where(some_query).first().run()

Sure, it takes a couple more key strokes, but sometimes consistency is king.

And lastly, perhaps the main way properties can be abused is if a really heavy piece of computation, or a long network request, is done to generate the response. A developer could unexpectedly cripple their app's performance by calling an innocent looking property too many times.

So in conclusion, properties can be great - but consider if you really need them, and if so keep them simple.

Posted on: 8 Aug 2019

Have any comments or feedback on this post? Chat with us on GitHub.

Allow Google Analytics?

This helps us improve the website. You can change your preference at any time.