Python underscores explained

Posted by Harald Nezbeda on Sun 13 May 2018

Python can sometimes look weird for starters, mostly when they have a background in another programming language. A common example is the underscore, which is used by python internally or in some well-established conventions across developers. Some of them are documented, but others you may come occasionally across the longer you write and maintain python code.

Conventions

There are two widespread conventions in python that imply the underscore. They are related to naming variables and methods. A leading underscore means that something was defined that is meant to be used only internally in a module.

class Example(Parent):
     _my_prop = 1234

Depending on your IDE or editor, you might get a warning when trying to access _my_prop on an instance of the class Example. Of course this is still valid Python code, but the convention is well known and this is why the IDE shows the warning.

In some cases you want to use a specific name for a method or property on a class, but the name might be already reserved by python and you will overwrite its behavior. In such a case you may better think of another name, but by convention developers decide to add a trailing underscore if there isn’t really a better name for what they need.

class Example(Parent):
    def my_func_():
        pass

To be fair, I’m not a big fan of this and I never really had to use it. But in case you find it in the python code of somebody else, you know at least what the intention of the developer was.

Also, a known convention is to use the underscore for internationalization and localization.

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))

Dunders

When using double underscores (or short dunders) you will get some interesting behavior Python. First of all there are the special methods in python that have reserved name that use a double leading and double trailing underscore.

class Example(Parent):
    def __init__(self, *args, **kwargs):
        pass

In case, you are not familiar, I would recommend going throw the documentation of the Python data model. It will help understand more of what python does with objects under the hood.

If you need a property or method that should be used only on the instance of your model, but you don’t want it to be accessible when another objects is inheriting you base model you can then use a double leading underscore.

class Example(Parent):
    def __my_func(self, *args, **kwargs):
        pass

This will actually not exclude the value on the child instance, but it will mangle the name, so it will include the class name in it.

class A(object):
    __prop_private = hallo
    prop_public = world

class B(A):
    pass

a = A()
b = B()

# regular
print(a. prop_public)
print(b. prop_public)

# mangeled
print(a. _A__prop_private)
print(b. _A__prop_private)

Single underscore

The single underscore has also a speacial meaning in python, and you should avoid it inside your python code (exept if you use it for translations). Where you should consider using it is in the REPL, as it makes sence in this condext. The underscore is actually returning the last value that was printed by python. This means you can access some computation after execution and later store it into a variable and use it for something else:

Undescore in python REPL