underscore variable
Python programming
Python syntax
Python tips
coding best practices

What is the purpose of the single underscore _ variable in Python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The single underscore _ in Python serves multiple purposes depending on context. It is most commonly used as a throwaway variable for values you intentionally ignore, as the last expression result in the interactive interpreter, and as a convention for "private" names with a leading underscore. It is also used in internationalization (i18n) as an alias for gettext. Understanding these conventions makes Python code more readable and idiomatic.

Throwaway Variable

The most common use — indicating a value you do not need:

python
1# Ignore values during unpacking
2first, _, last = "John Michael Smith".split()
3print(first, last)  # John Smith
4
5# Ignore multiple values
6first, *_, last = [1, 2, 3, 4, 5]
7print(first, last)  # 1 5
8
9# Ignore in loops
10for _ in range(5):
11    print("Hello")  # Loop 5 times, index not needed
12
13# Ignore in tuple unpacking
14for _, value in enumerate(items):
15    print(value)  # Index ignored
16
17# Ignore function return values
18_, extension = os.path.splitext("file.txt")
19print(extension)  # .txt

Using _ signals to other developers that the value is intentionally unused.

Interactive Interpreter Result

In the Python REPL, _ holds the result of the last expression:

python
1>>> 2 + 3
25
3>>> _
45
5>>> _ * 2
610
7>>> _
810
9
10>>> "hello".upper()
11'HELLO'
12>>> _
13'HELLO'

This only works in the interactive interpreter, not in scripts.

Loop Variable (Unused Index)

python
1# Run something N times without using the counter
2for _ in range(10):
3    do_something()
4
5# List comprehension with unused variable
6zeros = [0 for _ in range(5)]
7print(zeros)  # [0, 0, 0, 0, 0]
8
9# Nested loops — outer index unused
10grid = [[0] * cols for _ in range(rows)]

Convention for "Private" Names

A single leading underscore indicates a name is intended for internal use:

python
1class User:
2    def __init__(self, name, age):
3        self.name = name         # Public
4        self._age = age          # Internal/protected by convention
5        self.__id = 42           # Name-mangled (double underscore)
6
7    def _validate(self):         # Internal method
8        return self._age > 0
9
10user = User("Alice", 30)
11print(user.name)      # "Alice" — public
12print(user._age)      # 30 — works but convention says don't access

The single leading underscore is purely a convention — Python does not enforce access restriction. However, from module import * does not import names starting with _.

python
1# module.py
2public_var = "exported"
3_private_var = "not exported by import *"
4
5# other.py
6from module import *
7print(public_var)    # Works
8print(_private_var)  # NameError — not imported by *

Internationalization (i18n)

python
1import gettext
2
3# Common convention: alias gettext as _
4_ = gettext.gettext
5
6print(_("Hello, World!"))  # Translatable string
7print(_("Welcome back"))   # Looked up in translation catalog

This is why some codebases use _() for translatable strings — it is a widely recognized convention from GNU gettext.

Digit Separator in Numeric Literals

python
1# Python 3.6+ — underscore as visual separator in numbers
2million = 1_000_000
3print(million)  # 1000000
4
5hex_val = 0xFF_FF_FF
6print(hex_val)  # 16777215
7
8binary = 0b1010_0011
9print(binary)  # 163
10
11pi = 3.14_15_93
12print(pi)  # 3.141593

The underscores are purely visual and do not affect the value.

Pattern Matching Wildcard (Python 3.10+)

python
1# match/case — _ is the wildcard pattern
2match command:
3    case "quit":
4        exit()
5    case "help":
6        show_help()
7    case _:          # Matches anything (like default in switch)
8        print(f"Unknown command: {command}")

In match/case, _ matches any value without binding it to a variable.

Common Pitfalls

  • Overwriting _ in loops before using it in the REPL: If you use _ as a loop variable, it overwrites the REPL's last-result value. In scripts this does not matter, but in interactive sessions it can be confusing.
  • Assuming _ prevents the value from being computed: _ = expensive_function() still calls the function and stores the result. The underscore is just a naming convention — it does not optimize anything away. The garbage collector frees it when _ is reassigned.
  • Using _ as a real variable: Some code uses _ as an actual meaningful variable (e.g., _ = some_important_value). This breaks the convention and confuses readers who expect _ to be throwaway. Use a descriptive name instead.
  • Conflicting with gettext in i18n projects: If your project uses _ = gettext.gettext, using _ as a throwaway variable in the same scope shadows the translation function. Use __ (double underscore) or dummy as the throwaway name in i18n codebases.
  • Expecting _ to be truly private: A leading underscore on class attributes (_name) is a convention, not enforcement. Any code can still access obj._name. Only name mangling with double underscores (__name) provides any protection, and even that can be bypassed.

Summary

  • _ as a throwaway variable signals that a value is intentionally ignored
  • In the Python REPL, _ stores the result of the last expression
  • Use _ in for _ in range(n) when the loop counter is unused
  • A leading _ on names (_var, _method) indicates internal/private by convention
  • _ is the wildcard pattern in Python 3.10+ match/case statements
  • Underscores in numeric literals (1_000_000) are visual separators with no effect on the value

Course illustration
Course illustration

All Rights Reserved.