What Do Single and Double Underscores Really Mean in Python?
This article explains the five underscore naming patterns in Python—single leading, single trailing, double leading, double leading and trailing, and a solitary underscore—detailing their conventions, the name‑mangling mechanism, and how they affect class attributes and imports.
Preface
This article introduces the various meanings and naming conventions of single and double underscores ("dunders") in Python, explains how name mangling works, and shows how these patterns affect your own Python classes.
Single leading underscore: _var
Single trailing underscore: var_
Double leading underscore: __var
Double leading and trailing underscore: __var__
Single underscore: _
A short cheat‑sheet summarizing the five underscore patterns is provided at the end.
1. Single Leading Underscore _var
A leading underscore is a convention that signals to other programmers that the variable or method is intended for internal use only; Python does not enforce any access restrictions. It also influences wildcard imports: names with a leading underscore are omitted unless the module defines an __all__ list.
“Hey, this isn’t really part of the class’s public interface. Ignore it.”
class Test:
def __init__(self):
self.foo = 11
self._bar = 23Instantiating the class shows that _bar is still accessible:
>> t = Test()
>>> t.foo
11
>>> t._bar
23When a module contains a name with a leading underscore, a wildcard import ( from my_module import *) will skip it:
# my_module.py
def external_func():
return 23
def _internal_func():
return 42 >> from my_module import *
>>> external_func()
23
>>> _internal_func()
NameError: name '_internal_func' is not definedRegular imports are unaffected:
>> import my_module
>>> my_module.external_func()
23
>>> my_module._internal_func()
42“A single underscore is a Python naming convention indicating the name is for internal use. It is not enforced by the interpreter.”
2. Single Trailing Underscore var_
If a desired identifier clashes with a Python keyword, appending a trailing underscore resolves the conflict:
>> def make_object(name, class):
SyntaxError: invalid syntax
>>> def make_object(name, class_):
... passThis convention is described in PEP 8.
3. Double Leading Underscore __var
Names that start with two underscores trigger name mangling: the interpreter rewrites the attribute name to include the class name, preventing accidental overriding in subclasses.
class Test:
def __init__(self):
self.foo = 11
self._bar = 23
self.__baz = 23Using dir() on an instance shows the mangled name:
>> t = Test()
>>> dir(t)
['_Test__baz', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_bar', 'foo']The original __baz attribute is accessible only via its mangled name _Test__baz. Attempting to access t.__baz raises AttributeError.
class ExtendedTest(Test):
def __init__(self):
super().__init__()
self.foo = 'overridden'
self._bar = 'overridden'
self.__baz = 'overridden' >> t2 = ExtendedTest()
>>> t2.foo
'overridden'
>>> t2._bar
'overridden'
>>> t2.__baz
AttributeError: 'ExtendedTest' object has no attribute '__baz'
>>> t2._ExtendedTest__baz
'overridden'
>>> t2._Test__baz
42Name mangling also applies to methods:
class MangledMethod:
def __method(self):
return 42
def call_it(self):
return self.__method() >> MangledMethod().__method()
AttributeError: 'MangledMethod' object has no attribute '__method'
>>> MangledMethod().call_it()
424. Double Leading and Trailing Underscore __var__
Names that both start and end with double underscores are not mangled; they are reserved for special Python constructs (e.g., __init__, __call__). It is best to avoid creating your own such names.
class PrefixPostfixTest:
def __init__(self):
self.__bam__ = 42
>>> PrefixPostfixTest().__bam__
425. Single Underscore _
A solitary underscore is conventionally used as a throw‑away variable, such as in loops where the index is irrelevant, or in unpacking to ignore values. In the interactive REPL it also holds the result of the last expression.
for _ in range(3):
print('Hello')
car = ('red', 'auto', 12, 3812.4)
color, _, _, mileage = car
# color == 'red', mileage == 3812.4
>>> 20 + 323
343
>>> _
343Summary
The following cheat‑sheet lists the five underscore patterns and their meanings:
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
