Important Shock

Adventures in Pythonic Encapsulation

Python has undergone a fair share of criticism for its lack of support for information hiding. Despite its being a solidly object-oriented language, Python has historically refused to support this facet of object-orientation. Other programming languages have implemented encapsulation in a variety of ways:

However, Pythonistas like myself often assert that “we’re all consenting adults here.” While this attitude is refreshing in this day of slavish devotion to OOP principles, the Python community has realized that in order to avoid alienating newcomers, Python should perform some sort of encapsulatory behavior. This article intends to show the various ways in which Python supports encapsulation and/or information hiding.

For the sake of argument, let’s implement a Student class:

class Student(object):

    def __init__(self, name):

        self.name = name

Easy enough, right? Now, let’s write a unit test. Note that it depends on the name attribute being stored as a string.

george = Student("George Washington")

print george.name.lower()

When run, this test will produce the following output: george washington
But what if *cue dramatic music* we changed the implementation of the Student class so that it stored the name variable as a list of strings?

class Student(object):

    def __init__(self, name):

        self.name = name.split()

Now, running the above unit test will give us the following error:
AttributeError: 'list' object has no attribute 'lower'
So how could we modify the Student class in such a way that people could interact with it as a string, despite it being stored in a list? Read on.

Method 1: Getters and setters
Python, being an underscore-happy language (not that there’s anything wrong with that!) allows for automatic variable-name-mangling with the addition of two preceding underscores; that is a fancy way to say that, in a Student class, a variable named __name will actually have the variable name _Student_name at runtime. Therefore, we could write the following code, using getters and setters (I’m using the Smalltalk-style naming conventions – foo accesses the variable foo, and setFoo sets it – for getters and setters here, though it applies equally well to Java’s getFoo and setFoo paradigm), and thereby encapsulate details of the class variables inside the class:

class Student(object):



    def __init__(self, name):

        self.__name = name.split()



    def name(self):

        return " ".join(self.__name)



    def setName(self, newName):

        self.__name = newName.split()

However, in order to get the unit test to work, we need to change it to this:

george = Student("George Washington")

print george.name().title()

After that, everything works dandily.

Advantages of this method:

Disadvantages of this method:


Method 2: __getattr__ and __setattr__

By overloading the __getattr__ and __setattr__ methods, one can intercept all calls to read or assign the value of an attribute belonging to a class. Using this, plus the double-underscore name-mangling paradigm, you can insert true, 100% garden-fresh private members. Here’s an example – it’s best written, not explained.

class Student(object):

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

    def __getattribute__(self, attr):
        if attr == "name":
            return " ".join(self.__dict__[attr])
        else:
            return object.__getattribute__(self, attr)

    def __setattr__(self, attr, value):
        if attr == "name":
            self.__dict__[attr] = value.split()
        else:
            object.__setattr__(self, attr, value)

Advantages of this method:

Disadvantages of this method:


Method 3: properties

Python 2.2 implemented a new style of attribute access, called properties. Simply put, they’re faster, easier ways to implement the __getattr__ and __setattr__ methods’ ways of transparent attribute gathering. Here’s an example:

class Student(object):
    def __init__(self, name):
        self.__name = name

    def getName(self):
        return " ".join(self.__name)

    def setName(self, newName):
        self.__name = newName.split()

    name = property(getName, setName)

Now, all assignment references to the name attribute will return properly. And, since we defined getName and setName, programmers who are used to the accessor-method paradigm can use those.

Advantages of this method:

Disadvantages of this method:


In conclusion:

These days, I’m using properties. They seem to be the best way to implement encapsulatory principles – and if the attribute methods get incorporated into the Python built-in object class, I shall never pine for Ruby’s attr_accessor again. And that’s a good thing, because I truly want Python to succeed.

Any inaccuracies? Complaints? Death threats? Compliments? Leave a comment!