Usage of variables starting with underscore in Python

This article explains the different patterns and uses of underscores in Python. We all know basic variable naming rules in Python – start with an alphabet or underscore, cannot start with a number, can use any alphanumeric characters and underscores, is case sensitive. For common variable naming use, underscores are used in snake_case variables – separating compound variable name with underscores (eg.  my_name_is_ralph) for every programming language. Now, in Python, underscore has a specific meaning for different situations. It has a slightly different meaning when used in ways other than for snake_case variables. At the end of this article, you will get a cheat sheet for easier future reference. The 5 basic underscore patterns in Python are –

  1. Single Underscore
  2. Single Leading/Pre Underscore
  3. Single Trailing/Post Underscore
  4. Double Leading/Pre Underscore
  5. Double Leading and Trailing Underscore

Let’s dive in and understand each of these with examples.

 

1. Single Underscore: _variable

Single underscore serves multiple purposes –

  1. Use in Interpreter – Underscore stores the value of the last expression evaluated by the interpreter in most Python REPLs (an interactive shell that takes single user input, evaluates it, and returns the result). It can then be used for further calculations and its value can be assigned to other variables also, just like any other variable.
    >>> 2+10
    12
    >>> _
    12
    >>> _ + 9
    21
    >>> _/3
    7.0
    >>> a = _
    >>> a
    7.0
    >>> a + _
    14.0
    >>>

     

  2. As a looping variable – Single underscores can be used as a variable in loops.
    >>> for _ in range(5):
    ...    print(_)
    ...
    0
    1
    2
    3
    4
    >>> for _ in ['Jade','Sally','Luke']:
    ...     print("Hello",_)
    ...
    Hello Jade
    Hello Sally
    Hello Luke
    >>>

     

  3. Use in unpacking expressions – You can use a single underscore in unpacking expressions as a “don’t care” variable to ignore values that you do not need for future use. It is a valid variable name used for this purpose. For example, we are unpacking a fruit dictionary. We are interested only in red fruits, but for unpacking, we need to assign all key-values contained in the dictionary to variables. This is where underscores come into play.
    >>> fruits = {'Yellow':['Banana','Mango'],
    ... 'Green':['Grapes','Guava'],'Red':['Apple','Cherry']}
    >>> _,_,red_fruits = fruits.values()
    >>> red_fruits
    ['Apple','Cherry']
    >>> _
    ['Grapes','Guava']
    >>>

     

2. Single Leading/Pre Underscore: _variable

Single leading underscores indicate that a name is meant for internal use. This convention is defined in PEP8. However, when we are writing our class, it behaves like any other variable/method name. It does not cause any error while instantiating the class and using its methods and variables. Take a look at the following example.

In this example, we see that there was no problem in instantiating the fruit class and accessing any of its methods or variables. This is because it is just a naming convention when it comes to variable/method names and does not affect the behavior of the program.

>>> class Fruits:
...   def __init__(self):
...     self.variety = 30
...     self._stock = "50 crates of each variety"
...   def _stage(self):
...     print("All fruits are fully ripe")
...
>>> check_fruits = Fruits()
>>> check_fruits.variety
30
>>> check_fruits._stock
'50 crates of each variety'
>>> check_fruits._stage()
All fruits are fully ripe
>>>

 

But single leading underscore affects how names get imported from modules. Let a module lady_bird.py have two methods – pattern(), _can_fly().

# Filename: lady_bird.py

def pattern():
  return "Red with black spots"

def _can_fly():
  return "Yes"

 

Now, if a wildcard import (import all classes in a package/all names in a module using import*) is used for this module, Python will import all the names except for those starting with a single leading underscore. But, unlike wildcard imports, regular imports are not affected by it.

>>> # Wildcard import
...
>>> from lady_bird import*
>>> pattern()
'Red with balck spots'
>>> _can_fly()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_can_fly' is not defined
>>>
>>> # Regular import using dot(.)
...
>>> import lady_bird
>>> lady_bird.pattern()
'Red with balck spots'
>>> lady_bird._can_fly()
'Yes'
>>>

 

3. Single Trailing/Post Underscore: variable_

Python keywords cannot be used as variable names. For example, class, def, len are all Python keywords. These cannot be used as variable names. To avoid naming conflicts, single trailing underscore can be used so class_ , def_ , len_ become valid variable names.

>>> def = "Python is a programming language"
  File "<stdin>", line 1
    def = "Python is a programming language"
        ^
SyntaxError: invalid syntax
>>>
>>> def_ = "Python is a programming language"
>>> def_
'Python is a programming language'

 

4. Double Leading/Pre Underscore: __variable

Double leading underscores in Python serve the purpose of name mangling. Name mangling is a technique where the interpreter rewrites an attribute name to avoid naming conflicts in the subclasses inherited from the parent class. This is not a naming convention. It has a special meaning to the interpreter. Any identifier of the form “__variable” (at least two leading underscores and at most one trailing underscore) is replaced with “_Classname__variable”, where “Classname” is the current class name. Let’s look at an example.

>>> class School:
...   def _no_teachers(self):
...     pass
...   def __no_students(self):
...     pass
...
>>> dir(School)
['_School__no_students', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_no_teachers']
>>>
>>> class ClassX(School):
...   def __no_students(self):
...     pass
...   def _topper(self):
...     pass
...
>>> dir(ClassX)
['_ClassX__no_students', '_School__no_students', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_no_teachers', '_topper']
>>>

As expected, the “_no_teachers” method in “School” class is not changed but “__no_students” is changed to “_School__no_students” in class “School”. In “ClassX”, “__no_sudents” method is changed to “_ClassX__no_students” hence preventing naming clash with the parent class. One important thing to note here is that “__variable” changes permanently to “_Classname__variable” and so we must access this variable/method name using the new name only.

 

5. Double Leading and Trailing Underscore: __variable__

Methods containing double leading and trailing underscores are special methods in Python called “magic methods” or “dunder methods”. Dunder here means “double underscore”. One can override these methods and define their special methods. For example, the len function internally calls the __len__ method of the object. We can define our special method, but it is better to avoid using double pre and post underscores to avoid any type of naming error.

>>> class School:
...   def __strength__(self):
...     return 2000
...
>>> st_joseph = School()
>>> st_joseph.__strength__()
2000
>>> dir(School)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__strength__', '__subclasshook__', '__weakref__']

 

 

Underscore Naming Patterns Cheat Sheet

Pattern

Example

Meaning

Single Underscore

_

Naming convention, stores value of the last exp evaluated by the interpreter, used as a variable in loops, used as don’t care variable for unpacking
Single Leading/Pre Underscore

_variable

Naming convention, not enforced by the interpreter, indicates the name is for internal use
Single Trailing/Post Underscore

variable_

Naming convention used to avoid naming conflicts with keywords
Double Leading/Pre Underscore

__variable

Enforced by the interpreter, causes name mangling for classes
Double  Leading and Trailing Underscore

__variable__

Naming convention, indicates special methods defined by Python, advisable to avoid

 

So, now we know the use of each underscore pattern in Python. At first, it might not sink in, seems like too much information to absorb. Do give it another read if you didn’t understand it at one go. It might not seem to be of much use to a Python beginner. But as you advance, you will realize that it is indeed an important part that helps to avoid some mistakes where you won’t be able to deduce what went wrong and eventually get a headache.

 

Want to add your thoughts? Need any further help? Leave a comment below and I will get back to you ASAP 🙂

 

For further reading:

Find the least frequent character in a string in Python
Find the GCD of two numbers recursively in Python
Python program to find smallest prime divisor of a number

Leave a Reply

Your email address will not be published. Required fields are marked *