Is It Pythonic to Use Properties to Limit Class Attribute Mutability?

immutabilitymutableprogramming practicespropertiespython

Some Explanation

I'm somewhat new to python and to programming (I've been at it for a little over a year). I just recently discovered python properties, and I've been using them to limit the mutability of my classes' attributes. It helps me or maybe other people who might use my code from misusing it. And because of the ways properties work in python, their mutability can be easily changed.

There's only one drawback that I can see: when I'm writing the doc for a method do I put it in the property declaration or in the method prescription?

My Questions

  • Is there any reason why I shouldn't be doing this?
  • If not, is it pythonic and why/why not?

A really simple example:

class SomeClass(object):
    @property
    def some_method(self):
        return self._some_method

    def _some_method(self):
         print 'something'

This way the some_method method of SomeClass is immutable.


And I've been using this pattern for all attributes of my classes:

  • mutable/immutable
  • variables/methods
  • "private"/"public", etc.

Best Answer

Is it pythonic to use properties to limit the mutability of class attributes (variables and methods)?

For attributes/properties/variables at either the class or instance level: yes, absolutely! The decorator @property is built in specifically to give control over read, write, and delete. Common use cases include:

  • enforcing read-only or write-only
  • ensuring valid values
  • ensuring consistency with other state
  • presenting limited internal state in multiple ways
  • triggering other system logic
  • persistence

But really the most authoritative I can get is to quote PEP-8, the Python style guide.

With this in mind, here are the Pythonic guidelines:

...

  • For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax.

For methods: no, not really. Most of the above usages don't typically apply to methods or functions. It's certainly possible to overwrite these dynamically at either the class or the object level, but it's not commonly needed. Doing so can certainly be ok, but it's unusual enough that it always bears closer scrutiny.

  • Is there any reason why I shouldn't be doing this?

First is readability. There's no reason to make one obvious line of code into six boilerplate lines just so you can have pass-through getters and setters. Python (thankfully!) was designed so that calling code has no idea whether an attribute is a raw attribute or a property. That means you can change it whenever is convenient for you, instead of being locked into the decision.

Second is readability. For the method hiding property, there isn't much value added, and that style isn't idiomatic. It takes much more effort to figure out what's going on in the first place. It's also awkward later when the method you think you're calling is called something else.

Third is convention. Looking specifically at your note about public/private, it seems you want a way to mark things public or private. But in your code sample, you're already correctly using a single leading underscore to indicate private content. As a reminder, it's possible to get at _private names simply by referring to them. It's just considered to be use at your own risk.

Fourth is a placeholder to show just how unimportant five is.

Fifth is performance. This is almost certainly not the source of any performance issue, but there's technically a very slight performance penalty for using a property. It's minuscule, and you shouldn't care about this unless your profiler has measured it and is absolutely certain this is the problem. It's not.

  • If not, is it pythonic and why/why not?

To sum up, I'd definitely stick with the Zen of Python's "Simple is better than complex."