No private keyword
Languages such as Java, C# and C++ have public and private keywords, and the compiler refuses to let outside code touch a private field. Python has no such keywords. Its philosophy is that programmers are adults: the language trusts you to read the documentation and respect a naming convention. Privacy in Python is a signal to other programmers, not a lock.
The three naming levels
nameis public. Anyone can read and write it._name, with one leading underscore, means "internal". It tells other programmers that the attribute is an implementation detail and may change without notice. Treat it as private.__name, with two leading underscores, triggers name mangling. Python renames it to_ClassName__nameinside the class, mainly to stop a subclass from accidentally reusing the same name.
A single underscore is only a hint
class Dog:
def __init__(self, name):
self._name = name
rex = Dog("Rex")
print(rex._name)
rex._name = "Max"
print(rex._name)
Rex
Max
Python does not stop either line. Nothing in the runtime enforces the underscore. The convention works because people follow it, and because tools and style guides flag code that reaches into another class's underscored names.
Double underscores rename the attribute
class Dog:
def __init__(self, name):
self.__secret = 1
rex = Dog("Rex")
print(rex._Dog__secret)
print(rex.__secret)
1
AttributeError: 'Dog' object has no attribute '__secret'
The attribute is stored as _Dog__secret, so the plain name is not found. This is not real security: anyone who knows the pattern can still reach it. Its purpose is avoiding collisions. If a parent class and its subclass both use __x, they get _Parent__x and _Child__x and do not overwrite each other. For ordinary "please do not touch this" intent, a single underscore is what Python code uses.
The same convention applies to methods
Underscores are not only for attributes. A method named _helper tells readers that it supports the class internally and is not part of what outside code should call. The reader learns what is safe to depend on from the names alone.
Giving access through a method
When an attribute is marked internal, the class usually offers a method to read it. That keeps outside code on a stable interface, so the class can later change how the value is stored without breaking callers:
class Dog:
def __init__(self, name):
self._name = name
def get_name(self):
return self._name
Python also has a built-in tool for this, the @property decorator, which lets a method be read like an attribute. A property with no setter even rejects assignment with an AttributeError. That is a topic for later; the exercise here uses a plain method.
The lesson's exercise
The starter code gives Dog a constructor that stores the name as self._name, and ends by printing "Dog's name: " joined with rex.get_name(). The class lacks the getter.
- Declare the method. Inside the class, at the same level as
__init__, writedef get_name(self):. Do not forgetself. - Return the attribute. In the body, write
return self._name. The underscore is part of the name: the attribute was stored as_name, andself.namewould not exist.
class Dog:
def __init__(self, name):
self._name = name
def get_name(self):
return self._name
rex = Dog("Rex")
print("Dog's name: " + rex.get_name())
Dog's name: Rex
The method uses return, not print. The caller builds a longer message from the value, which only works if the method gives the name back.
Common mistakes
- Reading
self.namewhen the attribute is_name. They are different names, and the first raisesAttributeError. - Expecting the underscore to block access. It does not. Code that writes to an underscored attribute from outside runs fine, and may break later when the class changes.
- Using double underscores for everyday privacy. Mangling makes testing and debugging harder, and it is meant for avoiding clashes between classes.
Key takeaways
- Python has no
publicorprivatekeywords. Privacy is a naming convention. - A leading underscore means "internal, do not rely on it". The runtime does not enforce it.
- A double leading underscore mangles the name to
_ClassName__nameto avoid clashes with subclasses. - A getter method such as
get_namegives outside code a stable way to read an internal attribute.