Question
In Python, what do single and double leading underscores before an object's name mean?
For example, what is the difference between names such as:
_name
__name
How does Python treat these names, and what do they communicate to other developers?
Short Answer
By the end of this page, you will understand what leading underscores mean in Python, the difference between _name and __name, when Python applies name mangling, and how these naming styles are used in real code to signal intent and avoid accidental misuse.
Concept
In Python, leading underscores in names are mainly about convention and intent.
Single leading underscore: _name
A single leading underscore usually means:
- "This is internal"
- "This is meant for use inside this module or class"
- "You can access it, but you probably should not unless you know what you are doing"
Python does not make _name truly private. It is only a naming convention.
Example:
class User:
def __init__(self):
self._token = "secret"
You can still do this:
user = User()
print(user._token)
That works. The underscore is a signal to developers, not a hard restriction.
A single leading underscore also matters for from module import *. By default, names starting with _ are not imported.
Double leading underscore: __name
A double leading underscore inside a class triggers name mangling.
Python changes the name internally to reduce accidental access or accidental overriding in subclasses.
Example:
class User:
def __init__(self):
self.__token = "secret"
Python internally rewrites __token to something like:
_User__token
So this will fail:
user = User()
# print(user.__token) # AttributeError
But this will work:
print(user._User__token)
This shows that double underscores do not create true privacy. They mainly prevent accidental name collisions.
Why this matters
This naming system helps developers:
- communicate which attributes are part of the public API
- separate internal implementation details from public usage
- avoid accidental overrides in inheritance
- write clearer, more maintainable code
In short:
_name= internal by convention__name= name-mangled inside classes to avoid collisions
Mental Model
Think of Python names like rooms in a building.
nameis a public room with an open door._nameis a room with a sign saying staff only. The door is not locked, but people are expected to respect the sign.__nameis a room whose label is changed behind the scenes, so it is harder to walk into by accident.
The important idea is that Python prefers trust and convention over strict access control. Instead of blocking access completely, it gives signals about what should and should not be used directly.
Syntax and Examples
Basic syntax
class Example:
def __init__(self):
self.public = 1
self._internal = 2
self.__mangled = 3
Accessing each attribute
obj = Example()
print(obj.public) # 1
print(obj._internal) # 2
# print(obj.__mangled) # AttributeError
print(obj._Example__mangled) # 3
What this means
publicis meant to be used freely._internalis a non-public attribute by convention.__mangledis name-mangled by Python.
Example with inheritance
class Parent:
def __init__(self):
self._value = "parent internal"
self.__secret = "parent secret"
class Child(Parent):
def __init__(self):
super().__init__()
self.__secret = "child secret"
child = Child()
print(child._value)
print(child._Parent__secret)
print(child._Child__secret)
Why this example matters
If both Parent and Child define __secret, Python mangles them differently:
_Parent__secret_Child__secret
That avoids accidental collisions between parent and child classes.
Step by Step Execution
Consider this example:
class Account:
def __init__(self):
self._balance = 100
self.__pin = 4321
account = Account()
print(account._balance)
print(account._Account__pin)
Step by step
- Python starts defining the
Accountclass. - Inside
__init__, two instance attributes are assigned. self._balance = 100- Python stores an attribute literally named
_balance. - No special transformation happens.
- Python stores an attribute literally named
self.__pin = 4321- Because it starts with two underscores and is inside a class, Python mangles the name.
- Internally, it becomes
_Account__pin.
account = Account()creates an instance and runs__init__.print(account._balance)works because_balanceexists with that exact name.print(account._Account__pin)works because that is the mangled internal name.
What would fail
print(account.__pin)
This raises AttributeError because the instance does not have an attribute literally named __pin. It was renamed internally.
Real World Use Cases
1. Marking internal helper methods
Libraries often use a single underscore for methods that are not part of the public API.
class Parser:
def parse(self, text):
return self._clean(text)
def _clean(self, text):
return text.strip().lower()
Here, parse() is public, while _clean() is an internal implementation detail.
2. Avoiding subclass name collisions
Double underscores are useful when a base class wants to protect an internal attribute from being accidentally replaced by a subclass.
class BaseLogger:
def __init__(self):
self.__prefix = "LOG"
A subclass can define its own __prefix without overwriting the base class's version.
3. Hiding module internals from wildcard imports
In modules, names with a single leading underscore are treated as non-public.
_helper_value = 10
public_value = 20
With:
from mymodule import *
_helper_value is usually not imported.
4. Building stable public APIs
When writing reusable packages, developers often expose only public names and keep underscored names flexible so they can change them later without breaking users.
Real Codebase Usage
In real projects, underscores are used as part of API design.
Public vs internal attributes
Developers commonly separate names like this:
class ReportService:
def generate(self):
data = self._fetch_data()
return self._format(data)
def _fetch_data(self):
return [1, 2, 3]
def _format(self, data):
return ", ".join(str(x) for x in data)
The public method is generate(). The underscored methods support it internally.
Guarding against accidental overrides
In frameworks and large class hierarchies, double underscores may be used when a base class must keep an internal attribute separate.
class Base:
def __init__(self):
self.__state = {}
This reduces the chance that a subclass accidentally reuses the same attribute name.
Encapsulation by convention
Python codebases usually rely more on conventions, documentation, and properties than on strict privacy.
Example with a property:
class Product:
def __init__(self, price):
self._price = price
@property
def price(self):
return self._price
Here, _price is internal storage and price is the public interface.
Testing and maintenance
In real code, developers may still access _internal names in tests or debugging, but they try not to depend on them unless necessary because those internals may change.
Common Mistakes
Mistake 1: Thinking _name is private
Broken assumption:
class Demo:
def __init__(self):
self._value = 10
obj = Demo()
print(obj._value) # This works
A single underscore does not block access. It only signals internal use.
Mistake 2: Thinking __name means true privacy
class Demo:
def __init__(self):
self.__value = 10
obj = Demo()
print(obj._Demo__value) # Still accessible
Double underscores use name mangling, not true private access control.
Mistake 3: Overusing double underscores
Many beginners use __name everywhere. That often makes code harder to debug and extend.
Use double underscores only when you specifically want to avoid name collisions in subclasses.
Mistake 4: Forgetting name mangling only applies inside classes
Name mangling happens for class attributes and methods with double leading underscores. It is not a general Python rule for every variable.
Mistake 5: Confusing __name__ with name mangling
Names like __init__, __str__, and __name__ are different. These are often called dunder names.
class Demo:
def __str__(self):
return "Demo object"
These are special Python-defined names, not private attributes.
Mistake 6: Using wildcard imports and expecting underscored names
from mymodule import *
Beginners may expect _helper to be imported too. Usually it is skipped because it starts with _.
Comparisons
| Name style | Example | Meaning | Enforced by Python? | Common use |
|---|---|---|---|---|
| Public | name | Intended for normal use | No restriction needed | Public API |
| Single leading underscore | _name | Internal by convention | No | Internal helpers, non-public attributes |
| Double leading underscore | __name | Name-mangled in classes | Partly, via renaming | Avoiding subclass collisions |
| Double leading and trailing underscore | __init__ | Special Python-defined name | Yes, special meaning | Magic methods / dunder methods |
_name vs __name
| Feature | _name | __name |
|---|---|---|
| Signals internal use | Yes | Yes |
| Directly accessible as written | Yes | No, usually mangled |
| Prevents accidental subclass collisions | No | Yes |
| Best for everyday internal attributes | Yes | Usually yes |
| Best when inheritance collisions are a concern | No | Yes |
When to use which
- Use
namefor public attributes and methods. - Use
_namefor internal details. - Use
__nameonly when name mangling is actually helpful. - Use
__init__-style names only for Python special methods defined by the language.
Cheat Sheet
Quick rules
name→ public_name→ internal by convention__name→ name-mangled inside classes__name__→ special Python-defined name
Examples
class Example:
def __init__(self):
self.value = 1
self._value = 2
self.__value = 3
Access:
obj = Example()
print(obj.value) # works
print(obj._value) # works
print(obj._Example__value) # works
# print(obj.__value) # AttributeError
Key facts
- Single underscore is a convention, not privacy.
- Double underscore triggers name mangling in classes.
- Name mangling helps avoid accidental collisions in subclasses.
- Double underscore on both sides, like
__init__, is a special method name, not a private name. - Names starting with
_are usually skipped byfrom module import *.
Rule of thumb
- Default to public names when something is part of the API.
- Use
_namefor internal implementation details. - Use
__namesparingly and only when inheritance-related protection is useful.
FAQ
Is _variable private in Python?
No. It is only a convention that says the name is internal and should not usually be accessed directly.
Is __variable truly private?
No. Python mangles the name, but it can still be accessed using its mangled form, such as _ClassName__variable.
Why does Python use conventions instead of strict private members?
Python emphasizes readability, trust, and developer responsibility. Conventions are often enough and keep the language flexible.
What is name mangling in Python?
Name mangling is when Python rewrites a class attribute like __value to something like _ClassName__value to avoid accidental name collisions.
Should I use double underscores for all internal attributes?
Usually no. Most internal attributes should use a single underscore. Double underscores are mainly useful when inheritance could cause collisions.
Is __init__ the same as __value?
No. __init__ is a special built-in method name with meaning defined by Python. __value is a regular name that gets mangled inside a class.
Why is _name skipped in from module import *?
Because Python treats names starting with a single underscore as non-public during wildcard imports.
Can subclasses access double-underscore attributes?
Not directly by the original name. They would need the mangled name, and each class gets its own mangled version.
Mini Project
Description
Create a small Python class that demonstrates public, internal, and name-mangled attributes. This project helps you see how underscore naming affects access, readability, and inheritance in practical code.
Goal
Build a class and subclass that show the difference between name, _name, and __name in action.
Requirements
- Create a base class with one public, one single-underscore, and one double-underscore attribute.
- Add a method that prints the values from inside the class.
- Create a subclass that defines its own double-underscore attribute with the same name.
- Instantiate the subclass and show which attributes are accessible from outside.
- Demonstrate the mangled names for both base and subclass attributes.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.