Python Private Methods: Naming Conventions and Name Mangling
python private methods: Learn how Python implements private methods through naming conventions and name mangling, and when to use them in your classes.
Python private methods are not enforced by the interpreter. Instead, they rely on naming conventions and a mechanism called name mangling to discourage accidental access. Understanding how python private methods actually behave is essential for writing classes that communicate intent clearly without pretending the language offers hard privacy guarantees.
What Private Means in Python
In many languages, private is a compile-time keyword that prevents external code from calling a method or reading a field. Python does not have such a keyword. There is no way to make a method inaccessible from outside the class. Every attribute and method on an object is reachable, often through object.__dict__ or direct attribute lookup.
What Python does provide is a strong convention. A name prefixed with a single underscore, such as _internal, signals that the method is for internal use. A name prefixed with two underscores, such as __private, triggers name mangling, which changes the attribute name at runtime. Both mechanisms are advisory, not mandatory.
Single Underscore vs Double Underscore
The single underscore prefix is purely a convention. It tells other developers that the method is not part of the public API and may change without notice. Tools like linters and IDEs often flag external access to such names, but the interpreter does nothing special.
class Service: def _connect(self): print("Connecting to remote service") def start(self): self._connect() print("Service started")
The double underscore prefix is different. It triggers name mangling, which rewrites the attribute name to include the class name. This prevents accidental clashes in inheritance hierarchies and makes direct access from outside the class awkward, though still possible.
class Service: def __connect(self): print("Connecting to remote service") def start(self): self.__connect() print("Service started")
Inside the class, self.__connect works normally. Outside the class, service.__connect raises an AttributeError because the actual attribute is stored as _Service__connect.
How Name Mangling Works
Name mangling rewrites any identifier of the form __name (at most two leading underscores, at most one trailing underscore) to _ClassName__name. The transformation happens at compile time, not at runtime. The class name is the name of the class where the method is defined, not the name of the class that calls it.
class Base: def __secret(self): return 42 def call_secret(self): return self.__secret() class Derived(Base): def __secret(self): return 99 def call_derived_secret(self): return self.__secret()
In this example, Base.__secret becomes _Base__secret, and Derived.__secret becomes _Derived__secret. The two methods are completely independent. call_secret on a Derived instance still calls _Base__secret, not _Derived__secret, because name mangling is based on the class where the method is defined.
This behavior is useful when you want to prevent a subclass from accidentally overriding a method that the parent class depends on internally. It is not a security boundary, but it does reduce the chance of subtle bugs.
Accessing Private Attributes from Outside
Because name mangling only changes the attribute name, you can still access the method if you know the mangled name. For example:
service = Service() service._Service__connect() # Works, but strongly discouraged
This is not a feature to use in production code. It exists because Python does not enforce privacy. The double underscore is a signal that the method is internal and subject to change. Relying on the mangled name couples your code to the class name and the implementation detail.
There are legitimate reasons to inspect private methods, such as in unit tests when you need to verify internal behavior. In that case, you can access the mangled name, but it is better to test the public interface whenever possible.
Private Methods in Inheritance
Name mangling affects how private methods behave in class hierarchies. If a base class defines a method with two leading underscores, subclasses cannot override it in a straightforward way. The subclass can define its own method with the same name, but it will be stored under a different mangled name, so the base class's internal calls will not invoke the subclass version.
class Base: def __init__(self): self.__setup() def __setup(self): print("Base setup") class Child(Base): def __setup(self): print("Child setup") child = Child() # Output: Base setup
This is intentional. The double underscore prevents accidental overriding when a base class needs to guarantee that its own internal logic runs. If you want to allow overriding, use a single underscore or a regular method name.
When to Use Private Methods
Use single underscore for methods that are internal but may be overridden or extended in subclasses. Use double underscore when you want to avoid name clashes in a complex inheritance hierarchy or when a method is a pure implementation detail that should not be overridden.
A common pattern is to use a double underscore for helper methods that are tightly coupled to the class's internal state, such as validation routines or formatting logic that should not be called from outside. Single underscore is appropriate for methods that are part of an internal API but might be customized by subclasses.
Avoid using double underscore for methods that are part of a public interface or that need to be called from outside the class. The mangling makes it harder to use and can confuse developers who expect normal method behavior.
Debugging and Testing Private Methods
Private methods are not invisible to the debugger or to test frameworks. You can call them directly using the mangled name, but doing so ties your tests to implementation details. If the class name changes, the mangled name changes, and your tests break.
A more maintainable approach is to test private methods through the public interface. If a private method contains complex logic that deserves its own tests, consider extracting it into a separate module-level function or a mixin class. This keeps the logic testable without relying on mangled names.
When debugging, you can inspect the __dict__ of an object to see the mangled attribute names. This is sometimes useful when you need to understand why an attribute is missing or why a method call behaves unexpectedly.
service = Service() print(service.__dict__) # May show '_Service__connect' if it were an attribute
Remember that private methods are a convention, not a contract. They do not protect against malicious access or accidental misuse. The main benefit is that they make the code's intent clear and reduce the chance of naming collisions in large class hierarchies.
Common Mistakes and Edge Cases
One common mistake is using double underscore on a method that is called from outside the class, then wondering why AttributeError occurs. The error is expected because the method name is mangled. If you need external access, use a single underscore or a public name.
Another edge case involves methods that start and end with double underscores, such as __init__. These are special methods in Python and are not mangled. Name mangling only applies to names with at most one trailing underscore. Names like __foo__ are reserved for language-defined operations and should not be used for custom private methods.
Also note that name mangling applies to attributes and methods alike. If you have a private attribute and a private method with the same name, they are stored under different mangled names, but that is rarely a problem.
Finally, be careful when using double underscore in dynamically created classes or when using getattr and setattr. The mangled name is determined at compile time based on the class definition, so you must use the fully mangled name when using these functions dynamically.
attr_name = "_Service__connect" method = getattr(service, attr_name)
This is rarely necessary and should be avoided in production code. It is better to design the class so that private methods remain truly internal and are not accessed from outside.