Pimping Python’s property()

A basic tenet of object oriented coding is encapsulation: expose the interface but hide the implementation.

Seductress that it is, Python makes it very tempting to violate this principle. Since all members of an object are public, it’s easy to access and modify them from outside. For example:

class Person:
    def __init__(self):
        self.age = None
jeff = Person()
jeff.age = 5 # maturity

Compare this with Java, where you can make a member private, and use “getters” and “setters”:

class Person {
    private int age;
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public static void main(String[] args) {
        Person jeff = new Person();
        jeff.setAge(5);
    }
}

The Java version has better encapsulation. If you decide later to change “age” to, say, a float, a string(!), or if you move it into another internally linked object, it’s no big deal. Just change the body of the getAge() and setAge() methods to do the typecasting or make the extra call, and you won’t need to change any other code. The implementation changes; the interface stays the same.

Of course, you could write getters and setters in Python, too, but you can’t enforce their use (data members are public, remember?). A better way is to do some magic using __getattr__ and __setattr__, although that could get messy, especially if you have a lot of names to intercept.

This has been bothering me for a while. Then I recently discovered the built-in function property(), which lets you rewrite the example above as follows:

class Person(object):
    def __init__(self):
        self._age = None
    def get_age(self):
        return self._age
    def set_age(self, value):
        self._age = value
    def del_age(self):
        del self._age
    age = property(get_age, set_age, del_age)
jeff = Person()
jeff.age = 5

Sweeeet. From the outside, nothing seems different: it still appears that you’re getting and setting age directly, just as before. But property() calls get_age and set_age transparently, giving you a layer that effectively provides encapsulation, even though you can’t tell from the outside. After all, that’s the whole point of encapsulation: you shouldn’t be able to tell from the outside.

In fact, this is actually better than how Java does it. A first version of Person might very well have age as a member which its users directly modify: this is intuitive and effortless, both conceptually and syntactically. Only later might you need to bring property() into the picture, as the object’s internals change or grow more complex. And that’s how it should be: more lines of code and more complexity only when you need it.

(Of course, you still can’t hide data members, but this still goes a long way towards better, though not complete, encapsulation.)

I can’t remember ever seeing property() mentioned anywhere in tutorials or introductory docs (it’s in the reference for built-in functions) That’s especially odd considering that one of Python’s strengths is its powerful object features. It would make sense to talk about this in the context of encapsulation. Plus I wonder how widespread the use of property() is in real world code.

Stuff like this is why I love Python.

Update: Some good comments over at reddit.

10 thoughts on “Pimping Python’s property()”

  1. Actually if you prefix member data with a double underscore it becomes effectively private – but don’t do that. :) As you will note, that combined with the property capability does provide a superior enforced encapsulation mechanism than java (and pretty much equivalent to C++). However, its rarely used in python and normally seen in code that is written by people who come to python via java or C++.

    Frankly in a dynamically typed language its simply not needed because changing the internal type of the data member often does not break most public usage of the class. (Good unit tests will confirm this for you.) You will typically find the (proper) use of properties whenever you have a design-by-contract concept where the class method needs to check and guarantee certain variants. Just like real life, this is generally the exception rather than the rule and getters and setters turn out to be a lot of useless extra code and a “Bad Idea” TM.

    So before going nuts with properties, just try accessing the data directly. Its ok – no one will make fun of you. Its a very “pythonic” thing to do. Then, on those rare situations where you do need more enforcement by your code – it will be very clear to other programmers that this matters because that property will stand out rather than just be one property amongst the many.

    Keep your code very short, obvious, and elegant. I came to Python via C++ (and still love C++ which it seems most python folk do not) and it took me a while to abandon all that extra code which, while necessary in C++ – in python, didn’t really deliver me any extra business value and the compiler/environment and my project really would never use it. Things are different in a strong statically typed environment. Lose the extra class code and invest in unit tests instead. You’ll be much happier. But its good to know about properties cause they are priceless when you really need them…

    Good luck!

  2. Getters and setters are pointless boilerplate. A good language whose aim was strong encapsulation would allow for some sort of declarations to automatically generate getters and setters, since 99.99..% of the time, they’re gonna be “getBlah() {return blah; }” and “setBlah(stuff) { blah = stuff; }”. Only in cases where implementations change (which really isn’t frequent at all for most kinda of applications), you should have the ability to swap out the default getters/setters for some function. I would go so far as to say that automatically created getter/setter functions should be the default, and customer overloaded ones should require the declaration.

    But for the half of the world who doesn’t suck at programming, discipline and respect for public interface allows us to keep ourselves very content with Python. And really, accessing members directly isn’t a bad thing in Python, because if you *do* need to change it to a custom getter or setter, you can just add a __getattr__/__setattr__ and get on with you life.

    (Python is great)

  3. “Actually if you prefix member data with a double underscore it becomes effectively private – but don’t do that.”

    Actually it doesn’t become private; __bar in class Foo is simply changed to _Foo__bar. This is not for encapsulation, but instead to prevent conflicts between classes and subclasses which both want fields with the same name which are not intended for public use. If you define “foo = Foo()” then you’ll still be able to say “foo._Foo__bar”.

  4. Ben and Eli: I understand the double underscore to work in the way that Eli described. In general, name conventions do help (not just to avoid name conflicts but also for the programmer) but they can’t enforce privacy. As you can tell, Ben, I came to Python from Java so I still learn things all the time about the pythonic way of doing things. =)

    Tic-Tacs: from what I’ve seen, __getattr__/__setattr__ seems like a much more popular way; I was wondering whether it was actually preferred or whether people generally just don’t know about property(). The potential issue I can see with __getattr__/__setattr__ is that the bodies can get cluttered if you’re trying to preserve an older interface with lots of types that have changed.

    Thanks for the interesting replies so far! I love hearing other people’s thoughts on this.

  5. Actually, Beginning Python from Novice to Professional by Magnus Lie Hetland, covers this topic on pages 184-188. Just thought you’d like to know.

    No idea if Learning Python covers this or not.

  6. I have had some troubles with properties and inheritance. If I redefine a setter in a subclass, it’s not called correctly unless the property() is redefined for the overriden setter. Same goes for getter… :(

  7. Phil:

    I ran into that problem a long time ago. It’s quite a Python wart. I don’t remember all the details, but basically the property is bound to the class it was created in and isn’t overriden for you automatically. Here’s the work-around:

    class Foo(object):
    def get_foo(self):
    return ‘foo’

    foo = property(lambda self: self.get_foo())

    class Bar(Foo):
    def get_foo(self):
    return ‘bar’

    print Foo().foo, Bar().foo

    This will print “foo bar” instead of “foo foo” if you used property as you usually do. All we do now is delay the evaluation of self until the property is actually called, so it picks up the correct subclass instead of being bound forever to the class it was instantiated in.
    return ‘bar’

  8. For better or for worse, python favors “making it easy to write good/solid code” over “making it impossible to write bad/unreliable code”. IMO, the latter seems a bit futile in any case, which is why I like python. With that in mind, one should not be overly concerned with things like the distinction between “effectively private” and “actually private”, since nobody competent is going to use the pattern spam._Spam__eggs and then be surprised to find a bug that could have been prevented by proper encapsulation. I’m not saying it’s mathematically impossible, but no, it’s just not going to happen, and if you are worried about that kind of thing, \just go back to Java okay?\ Just sayin….

Leave a Reply

Your email address will not be published. Required fields are marked *