2025, Dec 07 15:00

Fixing AttributeError when accessing double-underscore (private) attributes across unrelated Python classes

Learn why name mangling causes AttributeError when accessing double-underscore attributes across classes in Python, and how to fix it safely or use public APIs.

Accessing double-underscore attributes across unrelated classes in Python looks tempting, but name mangling will get in the way. If one class holds an instance of another and you try to reach into that instance’s private fields, you’ll hit an AttributeError. Below is a minimal, practical walkthrough of what happens and how to fix it correctly when you really must.

The setup

We have two public classes. The first exposes its length via a dunder method and stores the value in a double-underscore attribute. The second receives an instance of the first and tries to copy that internal value into its own private field.

Problematic code

class Alpha:
    def __init__(self) -> None:
        self.__count: ...
    def __len__(self) -> int:
        return self.__count
class Beta:
    def __init__(self, source: Alpha) -> None:
        self.__source = source
        self.__count = __source.__count
    def __len__(self) -> int:
        return self.__count

This looks straightforward, but it fails at runtime with an AttributeError triggered by name mangling.

Why the AttributeError happens

Python applies name mangling to identifiers that start with two underscores inside a class body. The pattern is predictable: __attr becomes _ClassName__attr. This is designed to avoid accidental overrides in subclasses and to signal that such attributes are for internal use.

In the example, __source inside Beta gets mangled to _Beta__source, and __count inside Alpha gets mangled to _Alpha__count. Attempting __source.__count inside Beta therefore does not refer to the parameter name you think it does; you are not accessing a local or a public attribute. You end up poking at a name-mangled attribute that doesn’t exist on the Alpha instance, which raises the error.

There is no parent-child or nesting relationship here. Beta simply receives an object of a completely different class. Reaching into that object’s double-underscore attributes is not “protected” or special; it’s an attempt to access a private implementation detail of another class.

Solution: reference the mangled name on the correct class

If you truly need to read that private attribute, you must spell it exactly as Python mangles it for the class that owns it. The fix is to replace the broken access with the correctly mangled name of Alpha’s attribute.

class Alpha:
    def __init__(self) -> None:
        self.__count: ...
    def __len__(self) -> int:
        return self.__count
class Beta:
    def __init__(self, source: Alpha) -> None:
        self.__source = source
        self.__count = source._Alpha__count  # correct name-mangled access
    def __len__(self) -> int:
        return self.__count

This explicitly targets Alpha’s private field using the name Python generates for it, which avoids the AttributeError.

Why it’s important to know this

Double underscores are not a general-purpose encapsulation tool for cross-class access. They are a signal and a mechanism to keep attributes internal to the class that defines them. If you want to access an attribute from outside a class, don’t make it private or even protected. That’s a design decision, not a technical workaround problem.

Moreover, you may not need name mangling at all. If the goal is to expose a size, calling len on the held instance avoids breaking encapsulation and keeps the data live. For example, computing length on demand in the consumer class ensures it reflects updates if the contained instance changes. The key point remains: private/protected attributes should only be used within the class they belong to.

Takeaways

Use double underscores only when you want to keep implementation details strictly internal to a class and accept the constraints that come with name mangling. When you truly must access a private field from another class, reference the correct mangled name on the owning class, as shown above. But before reaching for that escape hatch, step back and consider whether the attribute should be public, or whether you can rely on a public API such as len(obj). That path avoids fragile, tightly coupled code and aligns with how Python expects you to express encapsulation.