If you come from languages like Java, C++, or C#, you’re used to having strict access control: private
, protected
, public
. But in Python, you might be surprised to find that private variables aren’t truly private.
In this article, we’ll break down:
- What “private” means in Python
- What
__double_underscore
really does - How it compares to other languages
- What Python intends — and how you should handle it
- Code examples that clarify it all
❓ Does Python Have Private Attributes?
Short answer: No, not really.
Python does not have real access modifiers like private
. Instead, it uses naming conventions and soft barriers.
🧪 Example 1: Normal Attribute (Public)
class Person:
def __init__(self):
self.name = "Alice"
p = Person()
print(p.name) # ✅ Accessible directly
There are no restrictions — Python assumes you know what you’re doing.
🔹 Example 2: _single_underscore
— “Protected” by Convention
class Person:
def __init__(self):
self._age = 30 # intended for internal use
p = Person()
print(p._age) # ✅ Still accessible
This is a warning to developers:
“This is an internal implementation. Please don’t touch unless you know what you’re doing.”
It’s not enforced by Python — purely conventional.
🔒 Example 3: __double_underscore
— Name Mangling
class Person:
def __init__(self):
self.__salary = 50000
p = Person()
print(p.__salary) # ❌ AttributeError
print(p._Person__salary) # ✅ Accessed using mangled name
✅ What Happened?
Python renamed __salary
to _Person__salary
to make accidental access harder.
This is called name mangling.
So it’s not private — just obscured.
🔧 Comparison: Python vs Java
Feature | Java | Python |
---|---|---|
private | Enforced | ❌ Not supported |
_var | Not allowed | ⚠️ By convention (protected) |
__var | N/A | 🔒 Name mangling only |
__var__ | N/A | ⚙️ Magic methods (dunders) |
🧠 Why Doesn’t Python Enforce Privacy?
Python follows a different philosophy:
“We are all consenting adults here.”
It trusts the developer to be responsible. You’re expected to follow conventions, not rely on enforced restrictions.
So rather than saying:
“You cannot access this.”
Python says:
“You shouldn’t access this — but I won’t stop you.”
✅ Best Practices in Python
🔸 Use _var
for protected-like fields
self._internal_id = 42
Use _
to indicate “don’t touch this unless you must.”
🔸 Use __var
when you want to avoid name clashes, not hide data
self.__config = {}
This is useful in inheritance, where subclasses might accidentally override your internal fields.
🔸 Use @property
to control access (like a getter)
class Bank:
def __init__(self):
self.__balance = 1000
@property
def balance(self):
return self.__balance
You can even add a setter:
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("Balance cannot be negative")
self.__balance = value
✅ Summary
Naming | Purpose | Access Control? |
---|---|---|
var | Public | ❌ No |
_var | “Protected” by convention | ❌ No |
__var | Name mangling | ⚠️ Yes (soft) |
__var__ | Special dunder methods (__init__ ) | ✅ Yes (Python magic) |
🏁 Final Thoughts
Python doesn’t enforce privacy like Java — and that’s by design.
- If you’re writing a library or shared API, use
_
and__
to signal intent - If you’re trying to protect values, use
@property
, validation logic, or even custom descriptors - And if you’re doing anything advanced — remember: name mangling is not encryption 😄
In Python, privacy is a gentleman’s agreement, not a jail cell.
Leave a Reply