The world’s leading publication for data science, AI, and ML professionals.

Why Python Loves Underscores So Much

Several tricks with underscores improve your Python code

Image by Manuel Enriquez from Pixabay
Image by Manuel Enriquez from Pixabay

Many Programming languages use underscore "_" in many scenarios, so does Python. If you have ever used Python for object-oriented programming, you must know that the constructor function of a Python object is __init__(). This is probably the most common scenario that we need to use underscores in Python.

However, there are much more cases that we can use one or more underscores to do some tricks. This could either improve our code in terms of reliability or even bring some new features. In this article, I’ll list several tricks of the underscores. Hope they could help.

1. Temporary Variable

Image by Tonda Tran from Pixabay
Image by Tonda Tran from Pixabay

The first trick I want to introduce is related to the temporary variable. When we don’t know the variable name or we don’t want to name a variable, the underscore can help us.

Last Result of REPL

REPL refers to "Read Evaluate Print Loop". When we run something in the Python console, the result will be buffered in the REPL as a variable. This variable does not have a name, but we can get it using an underscore.

The most typical example is the Jupyter Notebook. Suppose we run something in a cell, then we can get the result using an underscore in the next cell.

However, it needs to be emphasised that the "None" value will not be buffered. Therefore, the value of the underscore will NOT be replaced as in the example below.

Anonymous Variable

As a rule of thumb, we can also use an underscore to name a variable when we don’t really have to name it. Usually, it simply because we don’t need to use the variable afterwards, but it has to be placed somewhere.

for _ in range(10000):
    # Do something 10000 times
    pass

In the above example, we just need a for-loop to run some code snippet 10k times. However, the loop round is not important. So, we can use an underscore as the index of the for-loop.

It is also worth mentioning that most IDE will know this "rule of thumb" so that the underscore will not be checked with its usage afterwards. Instead, if we use a named variable in this for-loop, some IDE tools may warn that we "defined a variable without using it".

Placeholder

Another common usage of the underscore as a temporary variable is to be a placeholder. This is commonly used when we want to extract value(s) from a tuple. See the example below.

my_tuple = (1, 2)
x, _ = my_tuple
print(x)

As shown, we get the first value from the tuple and assign it to the variable x. The second one is ignored because we use an underscore as the "placeholder".

Another trick that you may not know is that we can use an asterisk with an underscore to be a "placeholder" for multiple values. Suppose we have a tuple with multiple values and we want to get the first and the last values from it.

my_long_tuple = (1, 2, 3, 4, 5)
x, *_, z = my_long_tuple
print('x =', x)
print('z =', z)

No matter how many values there are in the middle, the x, *_, z will always get the first and the last values.

2. Improves Readability

Image by Pexels from Pixabay
Image by Pexels from Pixabay

This one is probably known by many developers, but I still want to put it here. This little trick improves the readability of numbers in the program to a large extend.

num = 1_000_000
print(num)

We usually use a comma to separate every 3 digits in a number so that we can read the number easily. In Python, we can’t use commas but we can use underscore that does the same thing.

Also, for some culture and custom that doesn’t group every 3 digits, don’t worry, we can literally put the underscore anywhere. It won’t affect the result either.

num = 100_0000
print(num)

3. Resolve Name Conflicts

Image by Steve Buissinne from Pixabay
Image by Steve Buissinne from Pixabay

The underscore is also commonly used to avoid conflicts in a namespace. Please be noticed that there is no definition regarding this at the interpreter level. So, this is again another "rule of thumb" usage of the underscore.

For example, we want to define a function to get the "class" of some products. If we write the code as follows, it will raise an error.

def print_class(class):
    print('Class:', class)

This is because the term class is one of the reserved keywords in Python. Specifically, Python thinks we are defining a class of objects, but the syntax is apparently wrong.

In this case, the rule of thumb is to put an underscore behind the variable as follows.

def print_class(class_):
    print('Class:', class_)
print_class('Premium')

Of course, we don’t have to use "class" as the variable name because it’s just a variable name. However, sometimes it will improve our code readability NOT to avoid the reserved keywords.

4. Private/Protected Attributes and Functions

Image by Here and now, unfortunately, ends my journey on [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2806957) from Pixabay
Image by Here and now, unfortunately, ends my journey on [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2806957) from Pixabay

The underscore can also be used to protect some attributes and functions that we don’t want the downstream to reference, or it is just unnecessary to let them be imported or called.

Protect Importing

For example, in a .py file, we can define a number of constants and functions. Then, this Python file means to be imported by others as a library. However, we may have some local helper functions in this file that shouldn’t be imported.

In this case, we can put an underscore before the constants or functions so that they won’t be imported by from ... import *.

Let’s create a file lib.py and put the following two functions into it.

def test_func():
    print("I'm a test")
def _test_func():
    print("I'm a _test")

Then, let’s import all from this file.

from lib import *

After that, we can verify that the test_func() is imported but not the _test_func().

However, please be noticed that this is rather a "convenient" resort the prevents us from importing unnecessary functions. It does NOT prevent the user to explicitly import it.

If we explicitly import the function, it will work.

from lib import _test_func

5. Protect Class Attributes/Functions

Image by Free-Photos from Pixabay
Image by Free-Photos from Pixabay

Not only importing, but we can also use the underscores to protect attributes and functions in a class from being called outside.

This is to be done by adding double underscores __ in front of the attributes or functions that need to be protected.

Let’s define a class called Person, then instantiate it.

class Person():
    def __init__(self, name, age):
        self.name = name
        self.__age = age
    def show(self):
        print(f"My name is {self.name} and I'm {self.__age} years old.")
    def __show(self):
        print(f"Hey {self.name}, you shouldn't call this function.")
p1 = Person('Chris', 33)

So, we can call the show() function to print the pre-defined string.

However, if we try to call the attribute __name and the function __show() that are "protected" by the double underscores, the error will be thrown.

Again, this "protection" is not restricted at the interpreter level. It is convenient if we want to hide something in a class from being called. But there is a way to explicitly called. Just add _<class_name> in front of them.

6. Magic Functions

Image by 萩原 浩一 from Pixabay
Image by 萩原 浩一 from Pixabay

Well, everyone knows magic functions, but it is very important so I don’t want to skip it. The __init__() function that was used in the previous example is a kind of magic function. Apart from that, one of the most important would be the __repr__() function in a class. It defines what to be output when we print an object. If you’re familiar with Java, this is almost equivalent to the toString() method.

Let’s define a class without the __repr__() function and try to print the object.

class Person():
    def __init__(self, name, age):
        self.name = name
        self.age = age
p1 = Person('Chris', 33)
print(p1)

It just tells us the class name and the memory address, which is not very helpful. Now, let’s define the __repr__() function and try again.

def __repr__(self):
        return f"My name is {self.name} and I'm {self.age} years old."

Summary

Image by Franz Bachinger from Pixabay
Image by Franz Bachinger from Pixabay

In this article, I have introduced several tricks related to the underscores in the Python programming language. Sometimes, it enables us to have certain capabilities, sometimes it improves readability and reliability. Also, it provides some conveniences to protect imports and class attributes/functions.

These tricks are worth knowing and are important sometimes in order to understand the code from others. Hope these tips would help.

Join Medium with my referral link – Christopher Tao

If you feel my articles are helpful, please consider joining Medium Membership to support me and thousands of other writers! (Click the link above)


Related Articles