SoFunction
Updated on 2024-11-13

Different uses of python underscores

In this article, we'll cover the different uses of the _ character in Python. Like much else in Python, we'll see that the different uses of "_" are largely a matter of convention. Here are a few of the different cases we'll cover:

  1. Single underscore (e.g. _)
  2. Name preceded by an underscore (e.g. _total)
  3. Name followed by an underscore (e.g. total_)
  4. Single underscore in numeric text (e.g. 100_000)
  5. Double underscores before the name (e.g. __total)
  6. Double underscores around the name (e.g. __init__)

I. Single underline (_)

Single underlining is usually used in 3 situations:

1. In the parsing program

The _name points to the result of the last statement executed in the interactive interpreter session. This is done first by the standard CPython interpreter, with other parsers following suit.

>>> _ 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
NameError: name '_' is not defined 
>>> 42 
>>> _ 
42 
>>> 'alright!' if _ else ':(' 
'alright!' 
>>> _ 
'alright!' 

2. As a name

This is somewhat related to the previous point where _ is used as a one-time name. This is for the benefit of people reading the code, where a certain name is assigned but not intended to be used. For example, you may not be interested in the actual value of the loop counter:

n = 42 
for _ in range(n): 
    do_something() 

3.i18n

We can see that _ can also be used as a function. In this case, it is usually used as the name of a function that performs internationalized and localized string conversion lookups. This seems to derive from and follow the rules for C. As seen in the Django documentation:

from  import HttpResponse 
from  import gettext as _ 
 
def my_view(request): 
    output = _("Welcome to my site.") 
    return HttpResponse(output) 

The second and third uses may conflict, so avoid using _ as a one-time-use name in any code block that also uses _ for i18n lookup and translation.

II. Name preceded by a single underscore (e.g. _total)

A single underscore before the name is used to specify that the programmer considers the name "private". This can be seen as a convention to make it easier for people reading the code to know that names starting with _ are for internal use. As the Python documentation states:

A name with an underscore (e.g. _spam) shall be considered a non-public part of the API (whether it is a function, method, or data member). It should be considered an implementation detail and is subject to change without notice.
* The reason it's a convention is that it actually has some meaning to the parser; if we import * from, we don't import names starting with _ unless we explicitly include them in a __all__ list of modules/packages.

III. Single underscore after name (e.g. total_)

A single underscore after a name is used to avoid the name obscuring another name, by convention of course. For example, if you wanted to name a certain format, to avoid obscuring Python's built-in formatting, you could name it format_.

IV. Single underscores in numeric literals (e.g., 100_000)

PEP 515 Index suggests extending Python's syntax so that underscores can be used as visual separators for grouping numbers in whole, floating-point, and complex numeric text, reasoning:

This is a common feature of other modern languages and can help improve the readability of longer words or texts whose values should be clearly separated into parts, such as words in byte or hexadecimal representations.

Therefore, we can perform the following operations::

# Decimal numbers grouped in thousands
amount = 10_000_000.0 
 
# Grouping hexadecimal addresses by word
addr = 0xCAFE_F00D 
 
# Grouping bits into half bytes with binary text
flags = 0b_0011_1111_0100_1110 
 
# Same, for string conversion
flags = int('0b_1111_0000', 2) 

V. Double underscores before names (e.g. __total)

The use of double underscores (__) before names (especially method names) is not a convention, it just has a special meaning for parsing programs.Python manages these names, and it is used to avoid names conflicting with names defined in subclasses. As noted in the Python documentation, any identifier of the form __spam (at least two leading underscores and at most one trailing underscore) is textually replaced by __classname__spam, where classname is the current class name, where the leading underscore is removed.

Take the following example:

>>> class A(object): 
...     def _internal_use(self): 
...         pass 
...     def __method_name(self): 
...         pass 
...  
>>> dir(A()) 
['_A__method_name', ..., '_internal_use'] 

As you can see above, _internal_use is unchanged, but __method_name is changed to __ClassName__method_name. Now, if you create a subclass of A, say B (bad, bad name), then you won't be able to easily override A's

>>> class B(A): 
...     def __method_name(self): 
...         pass 
...  
>>> dir(B()) 
['_A__method_name', '_B__method_name', ..., '_internal_use'] 

The expected behavior here is almost equivalent to final methods in Java and regular (non-virtual) methods in C ++.

VI. Double underscores before and after the name (e.g. __init__)

These are special method names used by Python. For us, this is just a convention, a way for the Python system to use names that don't conflict with user-defined names. We then typically override these methods and define the desired behavior for when Python calls them. For example, __init__ overrides methods when writing classes.

There's nothing stopping us from writing our own special method names (but it's best not to):

>>> class C(object): 
...     def __mine__(self): 
...         pass 
... 
>>> dir(C) 
... [..., '__mine__', ...] 

Try not to use this naming convention, and just have the special names defined by Python follow it.

The above is the role of python various underscores in detail, more information about python underscores please pay attention to my other related articles!