Hi, I'm Harlin and welcome to my blog. I write about Python, Alfresco and other cheesy comestibles.

Python - Using @property and @setter for Data Encapsulation

I came to Python after coding in Java for a couple of years. That wasn't a very long to learn the ins and outs of Java or any other programming language but it was my first one where I had built up a measure of proficiency. When it comes to idioms, Java is not much different than C++ or C really. So, it's kind of a given that when you come over to Python from Java you will try to approach writing code in much the same way.

I was handling lists and tuples but still accessing them through while statements and with my_list[i]. For me at the time, Python's for statement seemed very awkward. It was too much like Perl's foreach, I thought. I eventually did come to understand the proper way to deal with lists after a time.

Eventually though, at work, I was spending a lot more time working in Java and decided that I had mastered Python -- it's so easy, right? So, I decided to spend more of my time getting to understand Java to a higher level. When you do something like this, it's easy to go back to "thinking in Java". As I would switch back to Python I would bring with me my Java habits.

Little by little, I had to learn things like: don't use camel case, make your if evaluations more natural looking, use list comprehensions and so forth. One area that I did not give much thought to was how Python handled data encapsulation with OOP.

You see, to me, Java's philosophy on data encapsulation was good practice for ALL programming languages.

If you're not sure what that is, one of the most common practices in Java is data encapsulation. From a very basic standpoint, the idea of data encapsulation in Java involves setting your class variables to private and then creating getters and setters for those class variables you need to make externally accessible for setting and retrieving values.

This has become so ingrained into Java that there are a large amount of 3rd party packages where you must implement getters and setters so that you can use the packages without too much mucking around. If you want to use Hibernate and JPA, you have to write your model classes with getters and setters.

Most Java IDEs will create these automatically for you but one of the things I like about Python is the idea that you should not have to write superfluous methods to get something to work.

To give you a quick example of what I'm talking about, consider this Java class called Thing:

public class Thing {

    private int x;

    public Thing(int x) {
        this.x = x;
    }

}

Because "x" is a private class variable, the App class (below) won't be able to directly access "x". So, the following code will throw an access error when trying to compile:

public class App {
    public static void main(String[] args) {

        Thing thing = new Thing(42);
        System.out.println(thing.x);

        thing.x = 25;
        System.out.println(thing.x);
    }
}

Below is a demo of how you would create getters and setters for a private variable in Java:

public class Thing {

    private int x;

    public Thing(int x) {
        this.x = x;
    }

    public int getX() {
        return this.x;
    }

    public void setX(int x) {
        this.x = x;
    }

}

And then the main method that will use these public methods:

public class App {
    public static void main(String[] args) {

        Thing thing = new Thing(42);
        System.out.println(thing.getX());

        thing.setX(25);
        System.out.println(thing.getX());
    }
}

This will print 42 and 25.

If we wanted to, we could also create getters and setters in Python. Here is how that would look:

#!/usr/bin/env python

class Thing(object):

    def __init__(self, x):
        self.__x = x

    def set_x(self, x):
        self.__x = x

    def get_x(self):
        return self.__x


if __name__ == '__main__':

    thing = Thing(42)
    print(thing.get_x())

    thing.set_x(25)
    print(thing.get_x())

So, it works but is this a good practice to carry over to your Python code? Actually, no. It's really not necessary. Java and Python are still very different from each other with different aims and goals.

Be aware that in Python there are class variables and instance variables. In Python the best practice is that class variables should be mostly static in nature and keep the same value between instances. On the other hand, an instance variable can be expected to change between instances.

I've seen a lot of Java programmers start writing in Python and then apply the getter and setter idiom to it. Even after writing Python code for about 5 years at the time, I did the same thing in some of my own projects.

With a public variable called 'x', we can simply access it directly. This works and what's great about it is that it's very readable:

#!/usr/bin/env python

class Thing(object):

    def __init__(self, x):
        self.x = x

if __name__ == '__main__':

    thing = Thing(42)
    print(thing.x)

    thing.x = 25
    print(thing.x)

Still, the data encapsulationists might complain that if you're directly accessing variables, then what about the need to add some functionality on assignment? For instance, what if when we set x, we want to make sure it is an even number or in a certain range or maybe you need to insert some validation code before hand?

That's a pretty fair point. So, if you must allow access to private variables and provide some intermediary functionality, the @property and @[var].setter decorator can be applied. Here is the same code essentially from the last code block but here we're assuming that x (or rather __x) is a private variable:

#!/usr/bin/env python

class Thing(object):

    def __init__(self, x):
        self.__x = x 

    @property
    def x(self):
        return self.__x

    # This will only work with a private member.
    # Otherwise, we will see a recursion error.
    @x.setter
    def x(self, x):
        if x % 2 == 1:
            x += 1
        self.__x = x


if __name__ == '__main__':

    thing = Thing(42)
    print(thing.x)

    thing.x = 25
    print(thing.x)

We can add these decorators and still access thing.x directly. This is very handy when you require some data encapsulation around your instance variables.

Remember that Python is a very mature language and has a lot of its own idioms that are going to be very different than other languages like Java, PHP or Ruby. In addition to learning new functions, libraries and frameworks, spend some extra time learning idiomatic Python.

These days, there are now a lot of articles and books on this very subject. If you spend some time on this subject and practice it, you will be writing much more professional looking code in no time. Hope this helps.

Any Comments, Always Welcome!