In object-oriented programming languages like Python, access modifiers or access specifiers are used to restrict access to class members such as attributes and methods. By controlling access, they prevent misuse or unintentional modification of a class’ internal representation.
Python handles access modifiers differently than languages like Java or C++. It has no explicit syntax for declaring a member private or protected. However, Python conventions and naming practices designate whether a member should be considered public, private or protected.
This guide will cover the following topics in using access modifiers in Python:
Table of Contents
Open Table of Contents
Public Members in Python
Public members in Python are attributes or methods that are freely accessible and modifiable by any part of the program. They serve as the external interface of a class.
In Python, any member not designated as private or protected is public by default. No special syntax is required to declare a public member:
class Person:
def __init__(self, name):
self.name = name
def say_hello(self):
print("Hello, my name is", self.name)
p = Person("John")
p.name = "Mary" # Modifying public attribute
p.say_hello()
The name
attribute and say_hello()
method are public and can be freely accessed and modified by any external code.
Advantages of public members:
- Provides a class API available to all code
- Allows flexibility in using and modifying the class
Disadvantages of public members:
- No access restrictions
- Implementation details exposed
- Class internals can be changed unintentionally
So public members are suitable when you want attributes and methods to be open for modification and use throughout the program. Access control should be implemented with private and protected members.
Private Members in Python
Private members limit access to class members from outside the class. This allows control over the internal representation and prevents accidental modification.
Python does not have a built-in private keyword like some OOP languages. Instead, it utilizes naming conventions to designate private members.
Any identifier prefixed with two underscores (__
) is treated as private:
class Person:
def __init__(self, name):
self.__name = name # Private attribute
def __private_method(self):
print(self.__name)
Trying to access __name
or __private_method()
from outside the class will fail:
p = Person("John")
print(p.__name) # AttributeError
p.__private_method() # AttributeError
This enforces privacy by making it harder to inadvertently access the identifiers from another part of the program.
Name Mangling
However, Python does not completely block access to such variables. Instead, it uses name mangling to modify the identifier name to include the class name.
So the actual mangled name for a private member would be _ClassName__identifier
:
class Person:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
p = Person("John")
print(p._Person__name) # Accessing mangled __name
The motivation behind this name mangling is to prevent accidental clashes between identifier names used in a subclass. So you can reuse a private identifier name in the subclass without modifying the superclass private member.
While name mangling allows access to private members, it should be avoided. It breaks encapsulation and harder to keep track of mangled names. Private members are meant for use only within the class definition.
Advantages of private members:
- Control access to class members
- Prevent accidental modification of class internals
- Allows reuse of identifier names in subclasses
- Flexibility in class implementation changes
Disadvantages of private members:
- Cannot be directly accessed or overridden by subclasses
- Name mangling makes it harder to identify class members
- Weaker form of privacy compared to other languages
So private members provide some access control for class internals. But they are not rigorously enforced in Python compared to languages like Java.
Protected Members in Python
Protected members allow access to class members within the class and its subclasses only. This provides selective access control between the class and its child classes.
In Python, protected members follow the naming convention of a single leading underscore _
:
class Person:
def __init__(self, name):
self._name = name # Protected attribute
def _protected_method(self):
print(self._name)
class Student(Person):
def get_name(self):
return self._name
So _name
and _protected_method()
can be accessed within Person
and Student
. But code outside these classes will get an attribute error if trying to access them.
Protected provides a middle ground between public and private access.
Advantages of protected members:
- Allow access and modification only in the class and subclasses
- Better versioning control than public members
Disadvantages of protected members:
- Not rigorously enforced - name mangling still allows outside access
- Can break encapsulation if subclasses misuse protected members
Protected members enable class extension and inheritance while limiting wider access. But they are also subject to potential misuse.
Access Modifiers Usage Considerations
Here are some key points to consider when deciding between public, private and protected members in Python:
- Private and protected have no absolute guarantee of blocking access. Name mangling allows outside access if identifier name is known.
- Python conventions rely on programmer discipline to not access private/protected members from external code.
- Mutable protected members can be modified by subclass methods, breaking encapsulation.
- Minimize use of public members to only what is required for external API.
- Use private members for sensitive internal data and methods that should not change.
- Limit protected members to what subclasses require access to. Avoid exposing unnecessary internals.
- Technology like closures can also restrict access to methods and attributes.
Overall, apply access modifiers judiciously based on your specific program requirements and constraints. Do not assume they provide absolute access restriction.
Example Use Cases
Here are some examples of appropriate use cases for the different access modifiers:
Public
- Methods intended for external use, like
calculate()
in aCalculator
class. - Attributes expected to be set or modified externally, like
name
in aPerson
class.
Private
- Class configuration/initialization details that shouldn’t be changed, like
__max_size
for a fixed sizeArray
class. - Sensitive credential data like
__user_password
. - Memory addresses or IDs that are internal implementation details.
Protected
- Methods in a superclass intended specifically for subclass override, like
__process()
in an abstractNetworkProcessor
class. - Structural metadata attributes accessed by subclasses, like
_vertices
in aGraph
class. - Hook methods allowing subclasses to customize behavior, like
_initialize()
method.
Best Practices
Below are some best practices to use access modifiers effectively in Python:
- Avoid public fields - Use properties to control attribute access
- Minimize public API surface area. Make methods private if they are not required externally.
- Use narrowed interfaces rather than wide public access.
- Utilize closures or nested functions for private helper methods not exposed on class.
- Prefix protected members with a single underscore
_
- Prefix private names with double underscore
__
- Use name mangling if identifier clashes occur for subclasses.
- Override private methods from superclass in subclasses using method name mangling.
- Document public APIs thoroughly. Explain protected/private members purpose without revealing sensitive internal details.
- Assume private and protected can still be accessed externally and protect integrity accordingly.
Pros and Cons of Access Modifiers in Python
Pros
- Provide some access restrictions for class members
- Prevent accidental misuse of class internals
- Allow control over class API available externally
- Enable reuse of identifiers in subclass hierarchy
- Flexibility in class implementation compared to strict access control
Cons
- Weak access restrictions compared to other languages
- Name mangling allows unintended access
- Implementation details still exposed
- Reliant on programmer discipline for access rules
- Mutable protected members may break encapsulation
Overall, Python access modifiers serve their purpose in most cases but have caveats compared to other OOP languages. Following conventions and best practices will provide reasonable access control for the public API, private internals, and protected subclass extensions of a Python class.