Encapsulation is by convention and name mangling, not enforced access control
Python has no private keyword. It relies on conventions: a single underscore prefix signals that an attribute is internal and subject to change, while a double underscore prefix triggers name mangling, where the compiler rewrites __attr inside a class body to _ClassName__attr. That mangling is not security; it prevents accidental override in subclasses but is still reachable if you know the mangled name. The real encapsulation tool in Python is a stable public API surface plus properties and slots to control access and mutation. The philosophy is that adults can be trusted; the language gives you tools to express intent rather than barriers to enforce it.
Single underscore: internal by convention, no language enforcement, no mangling.
Double underscore: name mangled to _ClassName__name, useful to avoid subclass collisions.
Dunder names (two underscores on both sides) are reserved by the language and should not be invented.
Use @property to expose a read-only or computed public attribute while keeping storage private.
Use slots to restrict which attributes can be set, which gives a degree of structural encapsulation and saves memory.
Trade-off: mangling can surprise users and complicate testing; many teams prefer a single underscore plus clear documentation.
Common mistake: thinking __attr makes data inaccessible. It only changes the name.
Version note: name mangling has been stable since Python 2 and applies to any identifier of the form __name inside a class body.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience