The Many Roles of the Underscore in Python
The underscore (_) character plays a surprisingly large number of roles in Python. From naming conventions to special interpreter shortcuts, understanding how and when to use underscores will make your code more expressive and Pythonic.
1. Ignoring Values You Don't Need
When unpacking, use _ as a throwaway variable for values you don't care about:
first, _, last = ("Alice", "B.", "Smith")
# We only need first and last
# Or ignore multiple values with *_
first, *_, last = [1, 2, 3, 4, 5]
# first=1, last=5
2. The REPL's Last Result
In the interactive Python shell, _ holds the result of the last evaluated expression:
>>> 42 * 3
126
>>> _ + 4
130
This is handy for quick calculations without reassigning to a variable.
3. Thousand Separators in Numbers
Python 3.6+ lets you use underscores as visual separators in numeric literals for readability:
population = 8_100_000_000
price_in_cents = 1_299_99
pi_approx = 3.141_592_653_589
Python ignores these underscores completely — they're purely for human readability.
4. Single Leading Underscore: "Internal Use Only"
A single leading underscore (_name) is a convention signaling that an attribute or function is meant for internal use and shouldn't be part of a public API:
class DataProcessor:
def process(self, data):
return self._clean(data)
def _clean(self, data): # "private" helper
return data.strip()
Note: This is a convention, not enforcement. Python won't stop you from calling _clean() externally.
5. Single Trailing Underscore: Avoiding Keyword Conflicts
When a variable name clashes with a Python keyword or built-in, add a trailing underscore:
class_ = "Mathematics" # 'class' is a keyword
type_ = "integer" # 'type' is a built-in
list_ = [1, 2, 3] # 'list' is a built-in
6. Double Leading Underscore: Name Mangling
Double leading underscores (__name) trigger Python's name mangling mechanism, making attributes harder to accidentally override in subclasses:
class Base:
def __init__(self):
self.__secret = 42 # becomes _Base__secret
class Child(Base):
def __init__(self):
super().__init__()
self.__secret = 99 # becomes _Child__secret (different!)
7. Dunder Methods: Double Leading and Trailing Underscores
Methods like __init__, __str__, __len__, and __repr__ are Python's "magic" or "dunder" methods. They define how objects behave with built-in operations:
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
Quick Reference Table
| Pattern | Meaning |
|---|---|
_ | Throwaway variable / last REPL result |
1_000_000 | Readable numeric literal |
_name | Internal / "private" by convention |
name_ | Avoid keyword/built-in conflict |
__name | Name mangling in classes |
__name__ | Dunder / magic method |
These conventions are part of what makes Python code feel idiomatic. Once you internalize them, you'll both write and read Python more fluently.