Have you ever seen underscores with an object name? Have you wondered what these underscores mean and why we need them? In this article, we will dive deep into Object Oriented programming in Python and try to understand the meaning of single and double underscores before an object name.
What is OOP(Object Oriented Programming)?
Object-oriented programming is a way of writing code. Our focus is to create classes that consist of attributes known as data members and functions to manipulate those attributes. OOP has properties like polymorphism, inheritance, encapsulation, and abstraction.
Related: Learn more about Object Oriented Programming in Python.
Let’s discuss all these properties of Object-oriented programming to understand it better.
Encapsulation is achieved in OOP by binding the data members, and the member functions together in a class stored in a single memory block. The member functions can access the data members of the class while hiding the data members from non-member functions of the class.
Related: Learn more about Encapsulation in Python.
Polymorphism is one interface and multiple methods. It refers to the ability of object-oriented programming to give multiple functionalities for a single interface. It promotes the DRY(Don’t repeat yourself) principle of object-oriented programming. Polymorphism can be achieved by operator overloading or function overloading. We must be careful while doing operator or function overloading as it may lead to ambiguity.
Inheritance is the process of acquiring properties and functions of a previously built class by inheriting the class. The class which inherits another class is called the child class, and the class which has been inherited is called the parent or base class. We can inherit multiple classes in the same class, acquiring properties and functions of all the inherited classes. Inheritance also promotes the DRY principle of OOP. Inheritance may also lead to ambiguity. We will talk more about ambiguity due to inheritance later in this article.
Related: Learn more about Inheritance in Python.
Abstraction in OOP is the ability to hide properties or restrict access to a class’s data members or member functions. It can be achieved by using access specifiers like private and public. The public access specifier allows a member to be accessed by the entire program, private allows only the member functions of the class to access the member for which the access specifier is used.
How to achieve abstraction in Python?
If you’ve used C++ or Java, they have these access specifiers like public and private, which can be used in OOP to achieve abstraction. Python doesn’t have any access specifiers. The reason behind this is that Python aims to have as simplistic syntaxes as possible so that the programmers can focus on other things rather than wasting time writing big syntaxes, which may lead to confusion and increase errors. The inventor of Python, Guido Van Rossum, once said, and I quote, “Abundant syntax brings more burden than help”.
Access specifier increases the complexity of the code and doesn’t contribute much to doing OOP efficiently. Therefore, all the data members and member functions are made public in Python by default. Now you must be thinking, “Wait, so does that mean we can’t perform abstraction in Python? That’s just disrespect of OOP. What’s the point of being an OOP language unable to perform abstraction?”
Don’t worry. Python won’t disappoint you here. Python has a different way of achieving abstraction. Python uses an underscore before the name of members to achieve abstraction. When you declare a member, putting a double underscore before the member’s name will make it inaccessible out of the class. It may sound complicated but stick with us; you’ll understand it easily.
Using leading single and double underscores for the privatization of a member
Leading Single Underscore
class sample_class: def accessible(self): print("You can access me out of the class' scope.") def _inaccessible(self): print("Don't access me out of the class' scope.")
In the above block of code, we used underscore before the inaccessible function. The reason behind this isn’t any functionality of Python. It’s just a nomenclature used by Python users to specify the class’s private members.
Leading Double Underscore
class sample_class: def accessible(self): print("You can access me out of the class' scope.") def __inaccessible(self): print("You cannot access me out of the class' scope :(")
In the above code block, we declared a class named “sample_class” first. Then we declared two functions in it.
First is accessible, and when we call the function, it will print, “You can access me out of the class’ scope.” This function is accessible out of the class scope cause it is declared normally, and all the class members are public by default in Python.
The second function is __inaccessible, and when we call it, it will print, “You cannot access me out of the class’ scope.” This function is not accessible out of the class scope cause we have started the function’s name with an underscore.
Now let’s test if the privatization of the function works or not.
First, we’ll create an object of the sample _class.
obj = sample_class()
Now, we’ll try to access the accessible function of obj.
Great! When we try to access the accessible function from the class scope, it prints, “You can access me out of the class scope.” Now let’s try to access the __inaccessible function out of the class.
It throws an Attribute Error. That means we did it. We cannot access the __inaccessible function out of the class.
Using Leading Single Underscore to access a private member of a class
When you check the above output properly, it says, “AttributeError: ‘sample_class’ object has no attribute ‘__inaccessible’ “. Why?
This method is not foolproof. What Python does is, instead of privatizing a function, it changes its name to _<classname>__<functionname>.
So in our case, the __inaccessible function’s name was changed to _sample_class__inaccessible. Now, let’s try to access it with this name.
So when we try to access the __inaccessible function with _sample_class__inaccessible(), we get the output “You cannot access me out of the class’ scope :(“. That means we can access it with a time name.
So we understood why we use a single underscore before an object name. Now let’s try to understand why we use a double underscore before an object name.
Using Leading double underscores for name mangling
When studying inheritance, we learned that inheritance might lead to ambiguity sometimes. It can be the case when we declare a parent and base class member with the same name.
Let’s try to understand with the help of an example.
class Parent: def member(self): print("I am a member of Parent class.") class Child(Parent): def member(self): print("I am a member of Child class.")
In the above code block, we created a class named Parent and declared a member function. We also declared a child class and declared a member function with the same name in it too.
child_obj = Child()
Created an instance of the Child class and named it child_obj. Now let’s try to access the member function from this child class object.
So when we try to access the member function from the child object with the same name as a member function in the inherited Parent class, the interpreter calls the member function of the child class.
How do we access the member function of the inherited parent class?
We can do this by using a double underscore before the member declaration in the class.
class Parent: def __member(self): print("I am a member of Parent class.") class Child(Parent): def __member(self): print("I am a member of Child class.")
Just like how we did in privatization, declaring a variable name with __ or double underscores before its name changes its name _<classname>__<functionname>. So due to this, we can access the member function of the parent class, which has the same name as the member function of the child class.
child_obj = Child() child_obj._Parent__member() child_obj._Child__member()
Trailing Single Underscore
A single trailing underscore in Python is a traditional nomenclature used to avoid name conflicts in Python. When we often want to name our variables or functions a particular word, a name conflict arises because there’s already a built-in function with the same name.
For example, you can’t name a list – list because the list is the name of the inbuilt object. In such cases, we can name the list as “list_”. This is a very common nomenclature used in Python. We use this nomenclature to name a class “class_” or an object as “object_”. It’s a good practice as we don’t waste time thinking of a name for our variable.
Trailing Double Underscore
There are no specific use cases for trialing double underscores in Python. Trailing double underscores alone makes no sense in Python. But in paired with leading underscores, it has a lot of functionality. Let’s discuss them.
More use cases of Double Underscore
Double underscore before and after a variable name symbolizes magic methods. Magic methods are also known as dunder methods. They are very common and utterly useful in Python. We don’t have to define magic methods, they are defined automatically by the interpreter. Let’s try to understand more about magic methods.
To check the dunder methods of a variable, use the dir function. Let’s check the dunder methods of a string.
So when we call the dir function on a string, we get all the functions of the string data structure. All the functions which are of type
__<function_name>__ are magic or dunder functions. These can’t be called directly. For example, the __add__ method specifies what will happen when you use “+” operator on a string. __mul__ is for multiplication, __len__ is for len function, etc.
The most essential magic method is the __init__ method. It is a constructor of the class. A class must have the __init__ method to initialize all the class data members.
Magic methods can be modified if wanted. For example, if you want the “+” operator to behave differently, you can modify the __add__ method as per your necessities. Magic methods are very important. Make sure you have a proper understanding of them.
Related: Learn more about magic methods in Python.
So we learned two methods that involve the usage of underscores before the object name. The first was for specifying the accessibility of a member, and the second was to call the member function of the parent class, which has the same name as the member function of the child class. At first glance, it may look hard to understand, but when you dig deep, you know it’s very simple yet important.
Stack Overflow answer for the same question.