DEV Community

Cover image for The Python Underscore _
Mike Vincent
Mike Vincent

Posted on

The Python Underscore _

Read this guide to learn how to use the underscore _ in Python.

The underscore _ in Python serves as a powerful naming tool that shapes how developers write and structure their code. This guide walks you through the many uses of underscores, from basic variable naming to advanced programming patterns.

History of the Underscore Character in Programming Languages

The underscore character originated in early programming languages as a way to create readable multi-word identifiers when spaces weren't allowed. In Python, it has grown into a versatile symbol with specific meanings based on its placement and count.

Meaning of Underscores in Python Variable and Method Names

Here's what the underscore tells us in Python names:

  • A single leading underscore (_name) hints at private variables
  • A trailing underscore (name_) helps avoid keyword conflicts
  • Double leading underscores (__name) trigger name mangling
  • Double underscores on both ends (__name__) mark special methods

These naming patterns stem from Python's design philosophy of readability. The underscore acts as a visual separator in names like user_id or get_name, making code easier to read and understand.

Underscore Conventions in Python

Python's underscore conventions follow the broader programming practice of using underscores to separate words in names - a style known as snake_case. This approach stands in contrast to other styles like camelCase used in languages such as JavaScript.

Other Uses of the Underscore in Python

The underscore's role extends beyond naming. It also works as a placeholder for unused values and helps format numeric literals for better readability.

The Role of Single Underscores in Python

Python's single underscore conventions serve two distinct purposes in code organization and naming.

1. Leading Single Underscore: Indicating Internal Use in Python

The single leading underscore (_variable) signals to other developers that a variable or method is meant for internal use within a module or class.

class Student:
    def __init__(self):
        self._internal_id = 12345  # Internal use only

    def _calculate_grade(self):    # Private method
        pass
Enter fullscreen mode Exit fullscreen mode

This naming pattern acts as a gentle warning sign - while Python doesn't restrict access to these elements, it suggests "please don't use this directly." The interpreter still allows external code to access these variables and methods.

2. Trailing Single Underscore: Avoiding Naming Conflicts in Python

The single trailing underscore (variable_) helps developers avoid naming conflicts with Python keywords. When you need to use a name that matches a Python keyword, adding an underscore at the end resolves the conflict:

class_ = "Python 101"  # 'class' is a keyword
from_ = "New York"     # 'from' is a keyword
import_ = "module"     # 'import' is a keyword
Enter fullscreen mode Exit fullscreen mode

The Python interpreter treats these names as regular variables while maintaining code readability. This pattern proves particularly useful when working with data processing or when translating code from other languages where these words might be valid variable names.

Understanding Double Underscores in Python

In Python, when you see a class attribute with two leading underscores, it means that a mechanism called name mangling is at play. This means that Python is automatically changing the name of that attribute to include the class name as a prefix.

How Name Mangling Works in Python

Let's take a look at an example to understand how name mangling works:

class Parent:
    def __init__(self):
        self.__secret = "hidden"

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.__secret = "not so hidden"

p = Parent()
print(dir(p))  # Shows '_Parent__secret'
Enter fullscreen mode Exit fullscreen mode

In this example, we have a Parent class with an attribute __secret. The Child class inherits from Parent and also defines its own __secret attribute. When we create an instance of Parent and call dir() on it, we can see that the __secret attribute has been transformed to _Parent__secret.

This transformation happens behind the scenes and helps prevent naming conflicts in inheritance hierarchies. When the Child class creates its own __secret attribute, it becomes _Child__secret, keeping it separate from the parent's attribute.

Why Use Name Mangling in Python?

Name mangling serves as a stronger privacy mechanism than using a single underscore. While Python doesn't enforce true private variables, name mangling makes it harder to accidentally access or override attributes from parent classes.

Here's an example to illustrate this:

class Database:
    def __init__(self):
        self.__password = "12345"

db = Database()
print(db.__password)  # Raises AttributeError
print(db._Database__password)  # Works, but strongly discouraged
Enter fullscreen mode Exit fullscreen mode

In this case, we have a Database class with a private attribute __password. When we try to access db.__password, it raises an AttributeError because the attribute is not directly accessible. However, if we really want to access it (which is strongly discouraged), we can do so by using the name mangling mechanism and referring to _Database__password.

Benefits of Name Mangling in Python

This feature proves valuable in large codebases where multiple developers work on class hierarchies. It helps maintain clean and predictable inheritance patterns by reducing the chances of accidental attribute overrides.

By using double underscores for attributes that need to be private, you can ensure that subclasses do not unintentionally modify or access those attributes, leading to more robust and maintainable code.

Special Methods and Dunder Names in Python

Python's special methods, wrapped in double underscores, shape how objects behave in the language. These "dunder" (double underscore) methods let you customize object behavior for built-in operations.

Creating Objects with __init__ in Python

The __init__ method creates a blueprint for new objects:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
Enter fullscreen mode Exit fullscreen mode

String Representation with __str__ in Python

The __str__ method defines an object's string representation:

class Book:
    def __str__(self):
        return f"{self.title} by {self.author}"

book = Book("1984", "George Orwell")
print(book)  # Output: 1984 by George Orwell
Enter fullscreen mode Exit fullscreen mode

Essential Dunder Methods in Python

Other essential dunder methods include:

# Compare objects
def __eq__(self, other):
    return self.value == other.value

# Make object callable
def __call__(self):
    return "I'm called like a function"

# Define length
def __len__(self):
    return len(self.items)
Enter fullscreen mode Exit fullscreen mode

These methods unlock built-in Python features:

  • __add__ enables the + operator
  • __getitem__ allows indexing with []
  • __iter__ makes objects work in for loops

The Python data model uses these special methods to integrate custom objects with the language's core features. Each dunder method serves a specific role in defining how objects interact with Python's built-in operations and functions.

Underscore as a Throwaway Variable in Python

Python developers often need a variable name for values they don't plan to use. The underscore (_) serves as this placeholder, making code cleaner and signaling to other programmers that the value holds no importance.

Unpacking Sequences in Python

A common use case appears in unpacking sequences:

name, _, age = ('John', 'Smith', 30)  # middle name ignored
first, *_ = ['apple', 'banana', 'cherry']  # keep first, discard rest
Enter fullscreen mode Exit fullscreen mode

Loop Iterations in Python

The underscore shines in loop iterations where the loop variable isn't needed:

# Print "Hello" 3 times without using the counter
for _ in range(3):
    print("Hello")

# Process dictionary values while ignoring keys
for _ in user_data.keys():
    process_data()
Enter fullscreen mode Exit fullscreen mode

Underscore REPL (Read-Eval-Print Loop) Usage in Python

In Python's interactive shell (REPL), the underscore takes on a special role. It automatically stores the result of the last expression:

>>> 2 + 2
4
>>> _  # returns 4
4
>>> _ * 2  # uses previous result
8
Enter fullscreen mode Exit fullscreen mode

This REPL feature helps during quick calculations or data exploration, letting users reference previous results without creating new variables. The underscore acts as an automatic memory of the last computation, streamlining the interactive coding experience.

Underscores in Numeric Literals in Python

Python 3.6 introduced a useful feature that allows us to use underscores in numeric literals. This means we can now include underscores in numbers to make them easier to read, especially when dealing with large values.

Why Use Underscores in Python?

When working with large numbers, it can be challenging to quickly understand their magnitude just by looking at the digits. By using underscores as visual separators, we can group the digits in a way that makes sense to us and improves readability.

Example:

  • Without underscores: 1000000000
  • With underscores: 1_000_000_000

In the example above, the underscore helps us instantly recognize that the number represents one billion (1,000,000,000) without having to count the zeros.

How to Use Underscores in Python

You can place underscores between digits in any numeric literal to achieve the desired grouping effect. Here are some examples:

# Without underscores
population = 1000000000
transaction = 1234567.89

# With underscores
population = 1_000_000_000
transaction = 1_234_567.89
Enter fullscreen mode Exit fullscreen mode

Grouping Patterns using Underscores in Python

The flexibility of this feature allows you to choose your own grouping style based on your specific needs. Here are some common patterns:

# Binary numbers
binary = 0b_1111_0000

# Hexadecimal
memory_address = 0x_FF_FF_FF_FF

# Scientific notation
planck_constant = 6.626_070_15e-34
Enter fullscreen mode Exit fullscreen mode

Restrictions on Underscores in Python

While using underscores in numeric literals is generally straightforward, there are a few rules to keep in mind:

  1. Underscores cannot be placed at the start or end of a number.
  2. Underscores cannot be placed directly next to a decimal point.

Here's an example that demonstrates these restrictions:

# Invalid examples
# leading underscore
invalid_amount = _12345

# trailing underscore
invalid_value = 3.14_

# adjacent decimal point and underscore
invalid_float = 1_23.45
Enter fullscreen mode Exit fullscreen mode

Python Underscore Compatibility

This feature works with all numeric types in Python, including integers, floats, and complex numbers. So whether you're dealing with whole numbers or decimal values, you can take advantage of this readability enhancement.

Here's an example showcasing its usage with different numeric types:

# Integer with thousands grouping
amount = 1_234_567

# Float with decimal and thousands grouping
price = 99_999.99

# Complex number with real and imaginary parts using grouping
complex_number = 2_5 + 3_4j
Enter fullscreen mode Exit fullscreen mode

By incorporating underscores into your numeric literals where appropriate, you can improve code clarity and make it easier for yourself and others to understand the significance of those values at first glance.

Internationalization and the Underscore in Python

The underscore symbol plays a key role in Python's internationalization (I18N) features. When building applications for global audiences, developers use the underscore as a shorthand alias for translation functions.

The most common use involves Python's gettext module:

from gettext import gettext as _

# Translation ready strings
greeting = _("Hello, World!")
error_msg = _("File not found")
Enter fullscreen mode Exit fullscreen mode

This naming convention comes from its widespread use in GNU gettext utilities. The underscore function wraps text strings, marking them for translation while keeping the code clean and readable.

A practical example in a multilingual application:

# settings.py
import gettext
gettext.install('myapp')

# main.py
def display_welcome():
    print(_("Welcome to my application"))
    print(_("Please select your language"))
Enter fullscreen mode Exit fullscreen mode

The gettext module searches for matching translations in language files based on the user's locale settings. The wrapped strings serve as keys to find appropriate translations in different language catalogs.

The underscore alias maintains code readability while enabling seamless integration with translation tools and workflows. This pattern has become a standard practice in Python internationalization, supported by major web frameworks like Django and Flask.

Best Practices for Using Underscores in Python

Python's PEP 8 style guide sets clear standards for using underscores in code. These naming patterns help developers write clean, readable, and maintainable code.

The guide recommends:

  • Use [snake_case](https://www.quora.com/Should-I-use-underscores-or-camel-case-for-Python) for function and variable names
  • Apply SCREAMING_SNAKE_CASE for module-level constants
  • Name class methods and instance variables with a single leading underscore to mark them as non-public
  • Reserve double leading underscores for cases where name mangling is needed
  • Keep dunder methods (__init__, __str__) unchanged and use them as designed
# Good practices
class Student:
    def __init__(self):
        self._internal_id = 123  # non-public
        self.__private_data = "secret"  # name mangling
        self.public_name = "Alice"  # public attribute

    def _internal_method(self):        # non-public method
        pass

# Bad practices
class student:  # class names should use PascalCase
    def __my_method(self):  # unnecessary use of name mangling
        self.publicName = "Bob"  # not using snake_case
Enter fullscreen mode Exit fullscreen mode

The Python community values readability. These naming patterns create a shared language among developers, making code easier to understand and maintain. Stick to these conventions unless you have a compelling reason to break them.

Changes to Underscores Over Python Versions

Python's use of underscores has evolved with each major version release.

Python 2 Underscore Changes

In Python 2, double underscores were introduced for name mangling. This feature enabled developers to create class attributes that are truly private.

Python 3.0 Underscore Changes

With the release of Python 3.0, stricter rules for name mangling were implemented. The interpreter now consistently applies name mangling to all names starting with double underscores, regardless of their position in the class hierarchy.

Python 3.6 Underscore Changes

A significant update came in Python 3.6 with PEP 515, which introduced support for using underscores in numeric literals:

# Old way
big_number = 1000000

# New way (Python 3.6+)
big_number = 1_000_000
Enter fullscreen mode Exit fullscreen mode

Python 3.7 Underscore Changes

Python 3.7 enhanced the use of underscores in the interactive shell. The _ variable now stores the result of the last expression evaluation, making it easier to work with previous results:

>>> 2 + 2
4
>>> _ * 2
8
Enter fullscreen mode Exit fullscreen mode

Python 3.8 Underscore Changes

In Python 3.8, a new syntax for positional-only parameters was introduced using a single underscore:

def greet(name, /, greeting="Hello"):
    return f"{greeting}, {name}"
Enter fullscreen mode Exit fullscreen mode

The latest versions of Python continue to follow these underscore conventions while also introducing new features that respect existing underscore naming patterns. This ensures backward compatibility for developers.

FAQs (Frequently Asked Questions) About Python Underscores

What is the significance of underscores in Python?

Underscores in Python serve multiple purposes, including indicating private variables, enhancing readability in numeric literals, and serving as an alias for translation functions. They play a crucial role in naming conventions and object-oriented programming.

What does a single leading underscore mean in Python?

A single leading underscore (e.g., _variable) indicates that a variable or method is intended for internal use only. It serves as a convention to signal that it should not be accessed directly from outside the class or module.

How do double underscores differ from single underscores in Python?

Double underscores (e.g., __variable) trigger name mangling, which changes the name of the variable to include the class name. This helps prevent name clashes in subclasses and is used for class-private attributes.

What are dunder methods in Python?

Dunder methods, short for 'double underscore' methods, are special methods defined by Python that allow you to implement behavior for built-in operations. Examples include init for object initialization and str for string representation.

How can underscores be used as throwaway variables?

In loops or functions, the underscore _ can be used as a throwaway variable when the value is not needed. This practice is common in the interactive Python shell (REPL) to indicate that a result is being ignored.

What best practices should I follow regarding underscores in Python?

According to PEP 8 guidelines, use single leading underscores for internal use indicators, double underscores for name mangling, and avoid using trailing underscores unless necessary to prevent conflicts with keywords. Additionally, use underscores in numeric literals to improve readability.


Mike Vincent is an American software engineer and writer based in Los Angeles. Mike writes about technology leadership and holds degrees in Linguistics and Industrial Automation. More about Mike Vincent

Top comments (0)