Underscores in Python

This blog post tackles the use of the _ in Python. You surely have seen variables like _foo or method names such as _bar or the most common baz. You may also have seen _ by itself. In this post we shall see the different usages of _ .

Single Underscore _

In the interactive interpreter

When running the interactive interpreter via python or ipython, the _ variable contains the result of the last executed statement.

>>> a,b=5,4
>>> a+b
9
>>> _
9
>>>

As a variable name

You may have seen code like this:

instance, _ = function_returns_a_tuple()

In the above code, the function returns a tuple of 2 values but the programmer is interested only in the first result, so he uses the _ to assign the second result to. The _ is used as a throwaway variable. We don't care what it contains but instead of putting a proper name of it, we use _ to signify to the next programmer reading our code that we do not use the second variable.

Another example is in a for loop. [Some might say looping like this is not pythonic but it illiustrates the point:

for _ in range(5):
     function_called_5_times_in_the_loop()

Used as a function name

For some reason the convention to create internationalization functions and use _ as the name has become the norm. This was copied from a corresponding C convention. We can see this in the django documentation for example:

from django.utils.translation import ugettext as _
from django.http import HttpResponse

def my_view(request):
    output = _("Welcome to jnvilo.com.")
    return HttpResponse(output)

Prefix A Variable Name with _

Have you ever seen variable names like:

class MyClass(object):
    def __init__(self, foo, bar):
         self._foo = foo
         self._bar = bar

In the above code we see the internal instance variables _foo, _bar starting with _. This is a convention used so that anyone using the class and reading the code knows that variables starting with _ are internal implementation only and can change in the next iteration of the MyClass.

[Error: Wrong macro arguments: '-info' for macro 'alert' (maybe wrong macro tag syntax?)]
a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.

  • I say kind of a convention because it actually does mean something to the interpreter; if you from <module/package> import *, none of the names that start with an _ will be imported unless the module’s/package’s all list explicitly contains them. See “Importing * in Python” for more on this.<</alert-info>>

Double Underscore Before a Name. The "Dunder" names.

The use of doubler underscores to prefix a varialbe name or method is not a convention. When the python interpreter encounters a method name starting with , it mangles these names to avoid clashes with names defined by subclasses.

>>> class A(object):
...     def __dunder(self):
...             print("I AM A")
... 
>>> dir(A)
['_A__dunder', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

The exercise above shows that dunder has been replaced with _A_dunder. If we now update our class A to call dunder() we can see:

>>> class A(object):
...     def call_dunder(self):
...             self.__dunder()
...     def __dunder(self):
...             print("I AM A")
... 
>>> A().call_dunder()
I AM A
>>

Nothing is different. Underneath python has replaced the self.__dunder() to self._A_dunder(). We can further see what happens when now A is subclassed by B.

>>> class B(A):
...     def b_calls_dunder(self):
...             self.__dunder()
... 
>>> B().b_calls_dunder()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in b_calls_dunder
AttributeError: 'B' object has no attribute '_B__dunder'

Python tried to call _B_dunder. It did not call the __dunder method defined in A.

Finally Double Underscores Before and After a Name.

You are not supposed to use these. These are special method names used by Python. This is however just a convention where Python system. Internal methods have the format foo , which you can then overrided in in your classes.

There is nothing to stop you from writing your own dunder methods like these but by convention, you should not. Only python defined special names should follow this convention.