Private, protected and public in Python

How does encapsulation (not) work in Python.
21 July 2011
~
2 min read

A lot of folks learn object-oriented programming with languages like C++ and Java. And while they learn, they're told repeatedly, that encapsulation is a key principle of the object-oriented paradigm, and that they should take advantage of it whenever they can. Sure, until you get down with Python.

In C++ and Java, the access modifiers are built into the language. However, there's no such a thing as public, protected and private in Python. That might be a little confusing, but it's possible to write object-oriented code regardless. In this post, we'll take look at how do just that.

Python doesn't have any mechanisms that would effectively restrict you from accessing a variable or calling a member method. All of this is a matter of culture and convention.

Public

All member variables and methods are public by default in Python. When you want to make your member public, you simply do nothing. Here's an example:

class Cup:
def __init__(self):
self.color = None
self.content = None

def fill(self, beverage):
self.content = beverage

def empty(self):
self.content = None

And here's what you can do with an instance of Cup:

redCup = Cup()
redCup.color = "red"
redCup.content = "tea"
redCup.empty()
redCup.fill("coffee")

All of this is good and acceptable, because all the attributes and methods are public.

Protected

Protected members (in C++ and Java) are accessible only from within the class and it's subclasses. How to accomplish this in Python? By following a convention. When you prefix the name of your member with a single underscore, you're telling others "don't touch this, unless you're a subclass". See the example below:

class Cup:
def __init__(self):
self.color = None
self._content = None # protected variable

def fill(self, beverage):
self._content = beverage

def empty(self):
self._content = None

It's the same example as before, but the content of the cup is now protected. This doesn't change anything: you'll still be able to access the variable from outside the class. But if you see something like this in a pull request:

cup = Cup()
cup._content = "tea"

You can politely explain to the author that the variable is protected, and that they should not access or change it from the outside of the class.

Private

Declaring your data member private means that nobody should be able to access it from outside the class, i.e. strong you can't touch this policy. Python supports a technique called name mangling. This feature turns every member name prefixed with at least two underscores and suffixed with at most one underscore into _<className><memberName> at run time.

So how to make your member private? Let's have a look at the example below:

class Cup:
def __init__(self, color):
self._color = color # protected variable
self.__content = None # private variable

def fill(self, beverage):
self.__content = beverage

def empty(self):
self.__content = None

Our cup now can only be filled and emptied by using the fill() and empty() methods. Note that if you try accessing __content from the outside, you'll get an error. However, you can still get around that like this:

redCup = Cup("red")
redCup._Cup__content = "tea"

When you see this in a PR, you should probably have a stern talking-to with the author.

Sources