In Python, there are several conventions used to indicate that a class member should be considered private. The most common way is to start the name with a single underscore (_). This guides developers to treat the member as being internal to the class, but does not actually prevent access to it.
Table of Contents
Open Table of Contents
- Overview
- Naming Convention to Indicate Private Members
- Purpose of Private Members
- Double Underscore Name Mangling
- Single Trailing Underscores
- Accessing Private Members Externally
- Getters and Setters for Private Members
- Inheritance and Private Members
- Private Members vs. Protected Members
- Best Practices for Private Members
- Example Class with Public and Private Members
- Conclusion
Overview
-
Python does not have a built-in way to enforce true private members like some other object-oriented languages. However, there are naming conventions used to indicate a member should be considered private and not directly accessed from outside the class.
-
Starting a member name with a single underscore (_) implies it is private. This is the standard convention in Python to designate a member as internal rather than public API intended for external use.
-
Double leading underscores (__) invoke Python’s name mangling which makes a member harder to access from outside the class. This is used mainly to avoid accidental collisions between subclass member names and parent class member names.
-
A single trailing underscore (self.member) is sometimes used to distinguish instance variables from local variables in methods.
-
Underscores in Python member names are only suggestions - there is nothing preventing external code from accessing members prefixed with underscores. But it goes against Python conventions and could break internal class details if changes occur.
Naming Convention to Indicate Private Members
The main convention used to indicate private members in Python is prefixing the name with a single underscore _
.
For example:
class MyClass:
def __init__(self):
self._private_member = "Hello"
def public_method(self):
print(self._private_member)
Here _private_member
is intended to be private to MyClass
. Code outside MyClass
should avoid directly accessing _private_member
and instead use the public public_method()
if access is needed.
This naming convention serves mainly as a hint to programmers that the member is internal to the class implementation and not intended for direct external use.
But Python does not actually prevent external code from accessing _private_member
:
myclass = MyClass()
print(myclass._private_member) # Still accessible from outside class
So underscores in Python do not enforce privacy, they simply guide programmers to treat the member as private as a matter of convention.
Explicitly declaring a member name with a single leading underscore _
at the start is the standard way to indicate it should be considered private in Python.
Purpose of Private Members
Some of the reasons to designate internal class members as private in Python include:
-
Prevent misuse or breakage: Private members contain internal implementation details that are more fragile and prone to issues if misused from outside the class.
-
Limit access and promote encapsulation: Classes should expose simple public APIs and keep complex internals private. This makes code more modular and maintainable.
-
Avoid naming collisions: Prefixing internal member names with
_
avoids namespace clashes between subclasses. -
Signal intent to programmers: Underscores immediately alert programmers a member is internal and not intended for public use per Python conventions.
So while Python cannot actively enforce true privacy, the naming convention serves an important purpose in guiding appropriate class design and use.
Double Underscore Name Mangling
Python utilizes a mechanism called name mangling when encountering member names with double leading underscores.
This converts the name to include the class name to avoid overriding by subclasses. For example:
class MyClass:
def __init__(self):
self.__private_member = "Hello"
print(MyClass._MyClass__private_member) # Name mangled
The double underscore __private_member
is mangled to _MyClass__private_member
when accessed externally.
Name mangling still does not guarantee privacy, but avoids naming collisions between superclass and subclasses using the same member names.
So while single underscores just signal privacy by convention, double underscores additionally invoke name mangling for collision avoidance.
Single Trailing Underscores
Sometimes a single trailing underscore is used on instance variables in class methods:
class MyClass:
def my_method(self):
my_var = "Hello"
self._instance_var = "Hi" # Trailing underscore
This does not impact privacy, but helps distinguish instance variables from local variables defined in methods.
The single trailing underscore is just a coding convention some developers use for readability.
Accessing Private Members Externally
While Python conventions recommend against it, there is no actual restriction from accessing members prefixed with a single underscore externally:
class MyClass:
def __init__(self):
self._private = "Hello"
myobj = MyClass()
print(myobj._private) # Still accessible
Since underscores do not enforce privacy, external code can still directly access the _private
member.
This breaks encapsulation and could cause issues later if internal details change. But Python leaves this responsibility up to the programmer.
For double underscore name mangling, the mangled name can still be used to access the member externally:
class MyClass:
def __init__(self):
self.__private = "Hello"
print(myobj._MyClass__private) # Accessed through mangled name
So while Python conventions recommend against accessing members starting with underscores externally, the language does not prevent it.
Getters and Setters for Private Members
A common way to allow controlled external access to private members is defining public getter and setter methods:
class MyClass:
def __init__(self):
self._private_member = "Hello"
def get_private_member(self):
return self._private_member
def set_private_member(self, value):
self._private_member = value
This allows external code to get or modify the value of _private_member
in a controlled way, without directly accessing the private member itself.
Getters and setters define the public API for accessing private data. This maintains encapsulation and allows the underlying implementation to change without impacting external code.
Inheritance and Private Members
When inheriting from a parent class, private members prefixed with a single underscore _
are accessible in the child subclass:
class Parent:
def __init__(self):
self._private = "Hello"
class Child(Parent):
def get_private(self):
return self._private
The Child
class can access _private
directly because private members are not enforced.
But subclasses should still treat parent private members as private per conventions. Indirect access via public methods is better for maintenance.
For double underscore __
members, name mangling ensures the subclass cannot override the parent’s member. But the mangled name still allows the subclass to access the parent’s private member if needed.
Private Members vs. Protected Members
Some languages like C++ distinguish between private members (class only access) and protected members (class and subclass access).
But in Python, there is no language-level difference between private and protected. By convention, both private and protected members start with a single underscore _
.
The distinction is based on intent - protected members are intended to be accessible in subclasses while private members are not. But Python itself does not enforce this.
Best Practices for Private Members
Here are some key best practices for using underscores and name mangling in Python:
-
Use a single underscore prefix
_
to indicate private members that should only be accessed within the class itself or via public methods. Avoid direct external access. -
Use double underscores
__
if additional name mangling is needed to avoid overriding in subclasses. -
Expose simple, intuitive public methods to provide controlled access to private members when needed.
-
Only access parent class private members from a subclass when absolutely necessary. Use public parent methods instead when possible.
-
Never rely on Python underscores for true data hiding or security. They are only naming conventions indicating intent.
-
Document private members thoroughly using docstrings and comments if they will be used by subclasses.
Following Python conventions for private members and avoiding direct access from external code will result in better encapsulated and more maintainable code.
Example Class with Public and Private Members
Here is an example class BankAccount
implementing public and private members using leading underscores:
class BankAccount:
"""Bank account class with private balance tracking."""
def __init__(self, name, balance=0):
"""Create new bank account."""
self.name = name # Public attribute
self._balance = balance # Private attribute
def deposit(self, amount):
"""Deposit funds into account."""
self._balance += amount
def withdraw(self, amount):
"""Withdraw funds from account if sufficient balance."""
if self._balance >= amount:
self._balance -= amount
return True
return False
def get_balance(self):
"""Return current account balance."""
return self._balance
BankAccount
provides a simple public API with deposit()
, withdraw()
, and get_balance()
methods that encapsulate the private _balance
member.
Client code should not access _balance
directly and instead use the public methods:
account = BankAccount("Sam", 500)
account.deposit(100)
print(account.get_balance()) # Access through public method
This class demonstrates proper encapsulation of private data using Python’s underscore naming convention.
Conclusion
In Python, a single leading underscore _
is the standard way to name internal class members that should be considered private. This convention serves an important purpose in marking members as internal implementation details not intended for direct external use.
Python does not actively enforce privacy of underscored members. But following this widely adopted naming convention signals programmer intent and promotes encapsulation.
Double leading underscores __
invoke name mangling which avoids accidental overriding by subclasses. Trailing underscores are sometimes used to distinguish instance variables from locals.
Underscores for indicating privacy in Python are a matter of idiomatic style. There are ways to access members marked private or mangled from external code. But this goes against conventions and loses the benefits of encapsulation.
Using Python’s namespace and naming conventions properly is an important part of API design and creating maintainable object-oriented programs. Understanding the purpose and proper use cases of underscores for marking class member privacy will lead to more robust and encapsulated code.