Private Methods in Python

Let me preface this article by emphasizing that understanding object-oriented programming (OOP) is crucial if you want to learn Python.

One aspect of OOP is to learn how to define and use private methods.

In this article, I will teach you how to create private methods, when to use them, and why they are necessary.

Warning: this is going to be a long in-depth article about private methods but if you only want to know how to define private methods in Python, here is the tl;dr.

tl;dr Prefix your attribute or method names with a single underscore. However, be aware that Python doesn’t support encapsulation, so nothing is really private.

Interested in becoming an expert in object-oriented programming in Python?

Check out my in-depth Python OOP course.

What are Private Methods?

A private method is a Class method that can only be called from inside the Class where it is defined.

This means that you can’t (and shouldn’t) access or call these methods from outside of the Class.

They are the opposite of public methods which are, as you might have guessed, methods that can be accessed from inside the class as well as outside the class.

Let’s look at a simple example of a private method in a programming language other than Python, say Java. (You will understand why later)

class Hello {
  private void printHello() {
    System.out.println("Hello world");
  }
}

In Java, the private keyword is used to declare a private method. So in this example, we define a private method called printHello() in class Hello.

If you try to call this method from outside of the class, it would just not work.

class Hello { 
  private void printHello() { 
    System.out.println("Hello world");
  } 
}

public class HelloWorld{
  public static void main(String []args){
    Hello h = new Hello();
    h.printHello();
  }
}

If you execute the above code, you get this self-explanatory compile error message.

$javac HelloWorld.java
error: printHello() has private access in Hello
  h.printHello();
   ^
1 error

However, you can still access the private method from within the class. Let’s define a new public method that accesses our private method internally.

class Hello {
  public void print() {
    printHello();
  }
  private void printHello() { 
    System.out.println("Hello world");
  } 
}

public class HelloWorld{
  public static void main(String []args){
    Hello h = new Hello();
    h.print();
  }
}

Now if you execute the above program, you won’t get any errors and you will get the string “Hello world” printed on the screen.

Notice that the private method printHello() was only called from within the class and the main program only called the public print() method.

Well, that’s cool and all. You probably knew that already. The main question is why do we need private methods in the first place? and when should we use them?

Let me explain.

When Should You Use Private Methods?

First things first, remember that encapsulation is one of the tenets of object-oriented programming (OOP).

But what is encapsulation really?

In object-oriented programming, encapsulation means hiding the internal implementation of an object from the outside world.

This means that the only way to interact with an object is through the object’s well-defined interface.

In other words, think of an object as a black box that you can interact with through a well-defined interface without having to worry about how the black box is actually implemented.

For example, imagine an object that represents a car, a simple car that only allows you to drive() it by pressing on the gas pedal or stop() it by pressing on the brakes.

In this case, we say that drive() and stop() are the interfaces between you (the user of the car) and the car instance.

You don’t have to worry about how the car actually moves or stops. This is not your problem. This is what the designer of the car cares about. For you, all you want to do is either drive or stop the car. This is what encapsulation is in a nutshell.

That’s cool and all, but let’s not talk about cars and talk about software now.

Let’s answer these two questions.

First: I mentioned that encapsulation hides the object’s implementation from the outside world. What is the outside world?

The outside world is basically other developers who are going to be using the Class that you are designing (it could be you).

It is NOT the end-user who is going to consume your binary (those will have either a user interface or a CLI), but encapsulation (at least in the OOP world) is about developer consumers.

Second: Why is encapsulation useful? why does it matter?

Great question. let’s go back to the car example that we explained earlier.

Imagine you want to replace the engine of your car with a newer, more powerful engine.

Do you (the user of the car) need to learn anything new to be able to drive the car after you had replaced the engine?

Absolutely not. Still, all you need to do is press on the gas pedal and the car will move.

This abstraction is critical in order to write maintainable code.

In other (more realistic) words, when you write your classes and libraries with solid, well-defined interfaces, this allows you (the designer of the class) to change the implementation details at a later time without affecting how other developers interact with your class.

This is because, as long as the interface doesn’t change, then everything is good. Nothing breaks.

However, if you don’t have a well-defined interface, then every time you change your code, you introduce a breaking change that will require all the users of your class to change their code. This is a code maintainability nightmare.

In object-oriented programming, you define this interface by declaring public attributes and methods.

And you achieve encapsulation by declaring private attributes and methods.

With this introduction, now we can start the article 🙂

Attributes and Methods in Python: A Refresher

Before moving to private methods and attributes, let’s start with a simple example.

Let’s construct a basic Class.

class MyClass:
  def __init__(self):
    self.var = 1

  def get_var(self):
    return self.var

In this example, we defined a Class called MyClass.

Inside this Class, within the __init__ method, we defined one attribute var that is initialized with the value 1 and a get_var method that returns the value of var.

Now let’s read and then change the value of var.

>> my_object = MyClass() # Create an instance of MyClass
>> my_object.var   # Call the attribute “var”
1                  # The output is “1”, as expected
>> my_object.get_var()
1
>> my_object.var = 2
>> my_object.var
2
>> my_object.get_var()
2

As you can see, we easily changed the value of var from 1 to 2.

We were able to do that because we have ACCESS to the attribute var from the outside.

But what if we want to hide this attribute from external users?

In the next section, I will teach you how to make this attribute private.

Defining Private Attributes and Methods in Python

To define private attributes or methods in Python, here is what you need to do.

Just prefix the name with a single underscore.

It is that simple.

Now let’s rewrite the above example to make var as well as get_var private.

class MyClass:
  def __init__(self):
    self._var = 1
  def _get_var(self):
    return self._var

Cool. Now let’s see what happens when we try to access this private attribute.

>> my_object = MyClass()  # Create an instance of MyClass
>> my_object._var  # Read _var
1
>> my_object._get_var()
1

Umm, wait! What?

I thought we can’t access private attributes or private methods! How is that possible?

Here me out, I promise I didn’t trick you 🙂

In Python, private access to private attributes (and methods) is not enforced.

So when we say that attributes which start with a single underscore are “private”, that is just a convention. But it is not enforced.

Python has no support for encapsulation. I love encapsulation, but the designers of the language decided not to support it. Oh well.

By saying that encapsulation is not enforced, that doesn’t mean you should access attributes and methods that are prefixed with an underscore.

Don’t do it because you are not supposed to.

In addition to that, if you are designing your own class or library, and you want to hint to your library users that they shouldn’t access specific attributes or methods, prefix the variable names with a single underscore.

Normally, this article should end here. However, there is one more thing I need to talk about before this article is fully complete.

We talked about what happens when you prefix method and attribute names with one underscore, but I am sure you have seen names with two leading underscores (and no trailing underscores).

What do these double leading underscores do?

Prefixing with Double Underscores (Name Mangling)

Let’s go back to our Class, and define our var and get_var(), but this time using double underscores instead of just one.

class MyClass:
  def __init__(self):
    self.__var = 1

  def __get_var(self):
    return self.__var

Now let’s see what happens when we try to access __var like before.

>> my_object = MyClass()
>> my_object.__var

AttributeError: 'MyClass' object has no attribute '__var'

It seems that Python can’t find the attribute __var, although we have just defined it in our Class.

Similarly, if you try to access __get_var(), you get the same error.

>> my_object.__get_var()

AttributeError: 'MyClass' object has no attribute '__get_var'

What’s going on?

One thing we could possibly do to investigate what’s going on is to print the content of dir(my_object)

dir() lists all the attributes (and methods) that an object has
>> dir(my_object)
['_MyClass__get_var', '_MyClass__var',...]

Interesting! It seems like Python renamed the variable __var and __get_var to _MyClass__var and _MyClass__get_var respectively.

This behavior of changing the attribute or method names is called Name Mangling.

Name Mangling makes it harder to access the variables from outside the Class, but it is still accessible through _MyClass__var.

>> my_object._MyClass__var = 3
>> my_object._MyClass__var
3

Similarly, you can access __get_var with its mangled name _MyClass__get_var.

In other words, outside the Class you would need to use the mangled name of the attribute, but inside the Class, you can still access the attribute in the normal way. (look how we accessed __var from inside __get_var())

It is worth mentioning though that some developers mistakenly use name mangling to denote private attributes and methods.

This is not right as this is not what name mangling is used for.

Name mangling is primarily used when you have inherited classes, but this is another story for another time.

What you should do is that you should follow the convention and use a single leading underscore to denote something as private.

Conclusion

To conclude, Python doesn’t support encapsulation. You should still prefix your private attributes and methods with one leading underscore so that users of your class know that they shouldn’t directly access these attributes. On the other hand, if you are the user of a class, don’t access attributes or methods that start with a leading underscore.

Learning Python?

Check out the Courses section!

Featured Posts

Are you Beginning your Programming Career?

I provide my best content for beginners in the newsletter.

  • Python tips for beginners, intermediate, and advanced levels.
  • CS Career tips and advice.
  • Special discounts on my premium courses when they launch.

And so much more…

Subscribe now. It’s Free.

Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments