NeuroAgent

Python Underscore Guide: Single vs Double

Complete guide to Python underscore conventions. Understand weak privacy indicators, name mangling mechanisms, and best practices for clean, maintainable code.

Question

What do single and double leading underscores before an object’s name represent in Python?

NeuroAgent

In Python, single and double leading underscores before an object’s name represent different levels of intended privacy and follow specific naming conventions. A single underscore (_variable) indicates a weak “internal use” marker, signaling to developers that the object should be treated as private and not accessed directly from outside the class, though this is purely a convention without actual enforcement. A double underscore (__variable) triggers name mangling, which automatically renames the attribute to include the class name (becoming _ClassName__variable), making it harder to accidentally override in subclasses but still technically accessible.

Contents

Single Underscore Convention

The single leading underscore (_) is used as a weak indicator of an object’s intended internal use. According to Real Python, this convention signals that the variable or method is meant to be private and shouldn’t be directly accessed from outside the class.

Key characteristics:

  • Convention-based, not enforced: Python doesn’t prevent access to underscore-prefixed names
  • Weak privacy: Acts as a hint to other developers about the object’s intended scope
  • Similar to Java’s protected: In programming language analogies, single underscore is often compared to Java’s protected modifier
  • Module usage: When used at the module level (not in classes), it indicates the variable is intended for internal use within that module

As Python Engineer explains, “Variables declared in class with single leading underscore are to be treated as private by convention, it works as a weak indicator to signify internal use only.”

Example:

python
class MyClass:
    def __init__(self):
        self._internal_value = 42  # Single underscore - internal use
    
    def _internal_method(self):
        return "This is internal"
    
    def public_method(self):
        return self._internal_method()

Double Underscore and Name Mangling

The double leading underscore (__) triggers Python’s name mangling mechanism, which is a more robust form of privacy protection. According to Real Python, “Python does name mangling automatically when you prefix an attribute name with two underscores (__).”

How name mangling works:

  • Transformation rule: __attribute becomes _ClassName__attribute
  • Class name inclusion: The current class name (with leading underscores stripped) is prepended
  • Automatic rewriting: The Python interpreter textually replaces the identifier during compilation
  • Limited scope: Only applies to names with exactly two leading underscores and at most one trailing underscore

The Python Enhancement Proposal 8 (PEP 8) explains this mechanism: “If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores.”

Technical details:

python
class MyClass:
    def __init__(self):
        self.__private_value = 100  # Will be mangled to _MyClass__private_value
    
    def __private_method(self):
        return "This is strongly private"

When you access these attributes from outside the class, you’ll find they’ve been renamed:

python
obj = MyClass()
print(obj._MyClass__private_value)  # This works - shows the mangled name
# print(obj.__private_value)  # This raises AttributeError

Practical Examples and Usage

Single underscore in practice:

python
class DatabaseConnection:
    def __init__(self, connection_string):
        self._connection = None  # Internal connection object
        self._is_connected = False
        self.connection_string = connection_string  # Public API
    
    def _connect(self):
        """Internal method for establishing connection"""
        self._connection = establish_connection(self.connection_string)
        self._is_connected = True
    
    def execute_query(self, query):
        """Public method that uses internal methods"""
        if not self._is_connected:
            self._connect()
        return self._connection.execute(query)

Double underscore with name mangling:

python
class BaseClass:
    def __init__(self):
        self.__private_data = "Base class private"
    
    def __private_method(self):
        return "Base class private method"

class SubClass(BaseClass):
    def __init__(self):
        super().__init__()
        self.__private_data = "Subclass private"  # Won't override base class
    
    def demonstrate_mangling(self):
        # Base class private data (mangled)
        base_data = self._BaseClass__private_data
        # Subclass private data (mangled)
        sub_data = self._SubClass__private_data
        return f"Base: {base_data}, Sub: {sub_data}"

Real-world name mangling scenario:

python
class Widget:
    def __init__(self):
        self.__internal_state = {}
    
    def add_state(self, key, value):
        self.__internal_state[key] = value
    
    def get_state(self, key):
        return self.__internal_state.get(key, None)

# Multiple inheritance scenario
class ClickableWidget(Widget):
    def __init__(self):
        super().__init__()
        self.__internal_state = {}  # Different from Widget's __internal_state
    
    def handle_click(self):
        # This won't interfere with Widget's __internal_state
        self.__internal_state['click_count'] = self.__internal_state.get('click_count', 0) + 1

When to Use Each Convention

Use single underscore when:

  • You want to indicate internal use without the overhead of name mangling
  • You’re working in a smaller codebase where team conventions are well understood
  • You need access to the attribute in subclasses (name mangling prevents this)
  • You want to avoid the slight performance overhead of name mangling

Use double underscore when:

  • You have a class that’s likely to be subclassed
  • You want to prevent accidental name conflicts in inheritance hierarchies
  • You need stronger protection against unintentional overrides
  • You’re working on a library or framework with many potential users

According to dbader.org, “A double underscore prefix causes the Python interpreter to rewrite the attribute name in order to avoid naming conflicts in subclasses.”

Common Misconceptions

Misconception: Double underscores make attributes truly private

Many developers believe that double underscores make attributes completely inaccessible, but this is incorrect. As Stack Overflow explains, “.__variable is often incorrectly considered superprivate this is somewhat misleading. The double underscores, while does name mangle, does so in an attempt to make it properly private.”

Misconception: Single underscore is enforced by Python

The single underscore is purely a convention. As Software Engineering Stack Exchange states, “Single underscores are not for module-private stuff specifically, they’re also used for a class’s private members. The difference between single and double underscores is only name mangling.”

Misconception: Double underscores provide security

Neither underscore convention provides actual security or privacy. As Reddit clarifies, “Python has no such mechanism. A double underscore prefix triggers name mangling.”

PEP 8 Recommendations

The official PEP 8 style guide provides clear guidance on underscore usage:

“If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores. This invokes Python’s name mangling algorithm, where the name of the class is mangled into the attribute name.”

PEP 8 also notes that:

  • Only two naming conventions enforce specific Python behaviors: double leading triggers name mangling
  • Single underscore is purely a convention indicating non-public visibility
  • Names with double underscores at both start and end (like __init__) are reserved for special methods

The Python Engineer blog summarizes the PEP 8 approach: “Variable names with a leading underscore are to be treated as private. This is just a convention, and Python doesn’t enforce it.”


Conclusion

Key takeaways:

  • Single underscore (_): Weak privacy indicator signaling internal use, purely a convention without language enforcement
  • Double underscore (__): Triggers name mangling, automatically renaming attributes to prevent subclass conflicts
  • Name mangling mechanism: Transforms __attr to _ClassName__attr, making accidental overrides less likely
  • Practical difference: Single underscore allows subclass access, double underscore provides stronger protection in inheritance scenarios

Action recommendations:

  • Use single underscore for internal methods and attributes that need some protection but might be accessed in subclasses
  • Use double underscore for classes that will be subclassed to prevent name conflicts
  • Never rely on underscore conventions for actual security - they’re hints, not barriers
  • Document your API clearly regardless of underscore usage
  • Consider using properties (@property) for controlled access to internal state

The underscore conventions in Python represent a thoughtful balance between flexibility and safety. While Python doesn’t enforce true privacy (unlike some other languages), these naming patterns provide clear signals about intended usage while maintaining the language’s “we’re all consenting adults here” philosophy.

Sources

  1. Real Python - Single and Double Underscores in Python Names
  2. PEP 8 - Style Guide for Python Code
  3. Python Engineer - What is the meaning of single and double leading underscore in Python
  4. dbader.org - The Meaning of Underscores in Python
  5. Stack Overflow - What is the meaning of single and double underscore before an object name?
  6. Software Engineering Stack Exchange - What is the historical reason why Python uses the double underscore for Class Private members