Back to Blog
Python

Python Double Underscore Name Mangling Explained

python double underscore name mangling: Learn how Python's double underscore name mangling rewrites attribute names inside classes, why it exists, and when to use it.

name manglingpython classesattribute privacyinheritancepython syntax
Diagram showing how Python rewrites a double underscore attribute name inside a class body.

What Python Double Underscore Name Mangling Does

Python's double underscore name mangling rewrites attribute names inside class bodies at compile time. When you write an attribute with two leading underscores, the compiler prepends the class name with a leading underscore. So inside a class named Counter, the attribute __count becomes _Counter__count.

class Counter: def __init__(self): self.__count = 0 def increment(self): self.__count += 1 return self.__count

Everywhere inside the class body, including inside methods, the compiler replaces __count with _Counter__count. The code above is equivalent to:

class Counter: def __init__(self): self._Counter__count = 0 def increment(self): self._Counter__count += 1 return self._Counter__count

The transformation is purely textual and happens when the class body is compiled. It does not change the runtime semantics of attribute access beyond the name change.

The Exact Transformation Rule

The mangling rule applies to any identifier that starts with at least two underscore characters, has at most one trailing underscore, and does not already end with two underscores. So __count is mangled, but __init__ is not, because it ends with two underscores. Names with two trailing underscores are reserved for Python's special methods and are left untouched.

The transformation is always the same: __name becomes _ClassName__name, where ClassName is the name of the class in which the identifier appears. The class name is inserted between the leading underscores and the rest of the identifier.

class Example: def method(self): __local = 1 print(__local)

Inside method, __local is rewritten to _Example__local. The local variable still works, but its name is different from what you wrote.

Where Name Mangling Applies

Name mangling only applies inside class bodies. At module level, a variable named __private is left alone. The mangling also applies inside nested classes, using the nested class's own name.

class Outer: class Inner: def method(self): self.__x = 1

Here self.__x becomes self._Inner__x, not self._Outer__x. The class name used is the one where the identifier appears, not the outermost class.

The mangling applies to all identifiers in the class body, not just attribute names. This includes method names, local variables, and parameters, though in practice it is most relevant for attributes and methods.

How Name Mangling Affects Inheritance

The main reason name mangling exists is to prevent accidental attribute collisions between a base class and a subclass. Consider this example:

class Base: def __init__(self): self.__value = 10 def get_value(self): return self.__value class Child(Base): def __init__(self): super().__init__() self.__value = 20

In Base, self.__value becomes self._Base__value. In Child, self.__value becomes self._Child__value. The two attributes are completely separate. Calling get_value() on a Child instance returns 10, because get_value reads _Base__value, which was set by Base.__init__.

Without name mangling, the Child.__init__ assignment would overwrite the base attribute, and get_value() would return 20. The mangling protects base class internals from being clobbered by subclass code that happens to use the same attribute name.

This matters most in large class hierarchies where you do not control the names that subclasses choose. A base class method that relies on self.__state keeps its own _Base__state even if a subclass also defines self.__state.

Practical Use Cases for Name Mangling

Name mangling is useful when you want to signal that an attribute or method is internal to a class and should not be overridden by subclasses. Framework code often uses it for this reason. If you are writing a base class that other developers will extend, mangling prevents them from accidentally overriding an internal helper method.

class BaseParser: def parse(self, text): self.__tokenize(text) return self._build_result() def __tokenize(self, text): self._tokens = text.split() def _build_result(self): return self._tokens

A subclass that defines its own __tokenize will not interfere with BaseParser.parse, because the base method is stored as _BaseParser__tokenize.

Name mangling is also useful when a class performs a lot of internal bookkeeping and you want to keep those attributes out of the way of subclass code. It is not a privacy mechanism, but it does reduce the chance of name collisions.

Inspecting Mangled Names at Runtime

Because the rewrite is textual, the mangled name is what actually exists on the instance. You can inspect it with dir() or __dict__.

c = Counter() c.increment() print(c.__dict__) # {'_Counter__count': 1}

You can also access the attribute directly using the mangled name:

print(c._Counter__count) # 1

This works, but it couples your code to the class name. If the class is renamed, the mangled name changes. External code should normally use the public interface instead.

Tools that inspect objects, such as debuggers, serialization libraries, and vars(), will show the mangled names. This is often surprising when you first encounter it.

Common Mistakes and Limitations

The most common mistake is expecting the original name to work. Accessing c.__count from outside the class raises AttributeError, because no attribute with that name exists. The attribute is stored under _Counter__count.

Another mistake is confusing name mangling with Python's special methods. __init__, __str__, and other dunder methods are not mangled because they end with two underscores. The rule explicitly excludes them.

Name mangling is not a security feature. Any code that knows the class name can access the mangled attribute. It only prevents accidental access and accidental overrides, not deliberate access.

The mangling also applies even when the attribute does not exist. If you write self.__missing in a method, the compiler still rewrites it to self._Class__missing. Accessing it raises AttributeError at runtime, but the name is still transformed. This can make debugging harder when you typo an attribute name inside a class.

When a Single Underscore Is the Better Choice

A single leading underscore is the Python convention for "protected" members. It signals that the attribute is internal but does not trigger any name rewriting. Subclasses can access and override it freely.

class Base: def __init__(self): self._value = 10 class Child(Base): def __init__(self): super().__init__() self._value = 20

Here Child overwrites the base attribute, and any base method reading self._value sees 20. This is intentional and often what you want when subclass authors are expected to customize behavior.

Use a single underscore when the attribute is part of an internal contract that subclasses may reasonably touch. Use double underscores when you want to make accidental override or access noticeably harder, and when the attribute is an implementation detail that subclasses should not rely on.

Maintainability and Compatibility Considerations

Name mangling has real consequences for maintainability. Because the mangled name includes the class name, renaming a class changes the attribute names stored on every instance. If you serialize instances with pickle or store their __dict__ in a database, the stored field names change when the class is renamed. Existing serialized data becomes incompatible.

The same applies to copy.copy, copy.deepcopy, and any code that inspects __dict__ directly. If you rely on vars(instance) to build a dictionary of fields, the keys will contain the mangled names.

Debugging also becomes slightly more involved. Tracebacks and debugger output show _ClassName__attribute instead of the name you wrote. This is usually easy to recognize, but it can confuse developers who are not familiar with the rule.

In practice, name mangling is best used sparingly. Reserve it for attributes and methods that are genuinely internal to a class and that you do not expect subclasses to override. For everything else, a single underscore communicates the intent without the extra rewriting.

python double underscore name mangling: Practical Usage and | RYUSLOG DEV