Python > Object-Oriented Programming (OOP) in Python > Encapsulation > Access Modifiers (public, protected, private - naming conventions)

Encapsulation and Access Modifiers in Python

This code snippet demonstrates encapsulation and access modifiers (public, protected, and private - through naming conventions) in Python using a simple `BankAccount` class. It illustrates how to control access to the internal state of an object.

Code Example

This code defines a `BankAccount` class with `account_number` (public), `_account_holder` (protected, using naming convention), and `__balance` (private, using naming convention) attributes. The `deposit` and `withdraw` methods modify the private `__balance`, while `get_balance` provides controlled access to it. The protected `_display_holder` shows a protected method as well.

class BankAccount:
    def __init__(self, account_number, account_holder, balance):
        self.account_number = account_number  # Public attribute
        self._account_holder = account_holder # Protected attribute (convention)
        self.__balance = balance           # Private attribute (convention)

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print("Deposit successful.")
        else:
            print("Invalid deposit amount.")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print("Withdrawal successful.")
        else:
            print("Insufficient balance or invalid amount.")

    def get_balance(self):
        return self.__balance

    def _display_holder(self):
        print(f"Account Holder: {self._account_holder}")

# Example Usage
account = BankAccount("1234567890", "Alice Smith", 1000)

print(f"Account Number: {account.account_number}") # Accessing public attribute
print(f"Account Holder (Protected - Convention): {account._account_holder}") # Accessing protected attribute - use with caution!

#print(f"Balance: {account.__balance}") # This will raise an AttributeError because __balance is treated as private.
print(f"Balance: {account.get_balance()}") # Accessing private attribute via getter method

account.deposit(500)
account.withdraw(200)
print(f"Balance after transactions: {account.get_balance()}")

account._display_holder() #accessing protected method.

Concepts Behind the Snippet

Encapsulation is the bundling of data (attributes) and methods that operate on that data within a single unit (class). It helps in hiding the internal state of an object from the outside world and preventing direct access to it. Access modifiers (simulated in Python through naming conventions) control the visibility of class members.

Access Modifiers in Python (Naming Conventions)

Python doesn't have strict access modifiers like `public`, `protected`, and `private` as in other languages (e.g., Java, C++). Instead, it uses naming conventions: * Public: Attributes and methods without any prefix are considered public. They can be accessed from anywhere. * Protected: Attributes and methods with a single leading underscore (e.g., `_attribute`, `_method`) are considered protected. This indicates that they are intended for internal use within the class or its subclasses. While Python doesn't prevent external access, it's a signal to developers that they should use these members with caution. * Private: Attributes and methods with a double leading underscore (e.g., `__attribute`, `__method`) are considered private. Python performs name mangling on these members, making them harder (but not impossible) to access directly from outside the class. The name mangling mechanism effectively changes the attribute's name to `_ClassName__attribute`.

Real-Life Use Case

Imagine a `DatabaseConnection` class. You might want to expose a public method to execute queries, but hide the database connection details (host, username, password) as private attributes to prevent unauthorized access and modification.

Best Practices

  • Use encapsulation to protect the integrity of your object's internal state.
  • Follow the naming conventions for access modifiers.
  • Provide getter and setter methods (properties in Python) for controlled access to attributes when necessary.
  • Avoid excessive use of private attributes. Sometimes, a protected attribute is sufficient.

Interview Tip

Be prepared to discuss encapsulation, access modifiers, and how Python simulates them using naming conventions. Explain the difference between public, protected, and private members and when to use each.

When to Use Them

Use encapsulation and access modifiers when you want to: * Control how data is accessed and modified. * Prevent unintended side effects from external code. * Hide implementation details. * Make your code more robust and maintainable.

Memory Footprint

Encapsulation itself doesn't directly affect memory footprint. However, using getter and setter methods can add a small overhead compared to directly accessing attributes. The memory footprint of the BankAccount class will primarily depend on the datatypes of account_number, account_holder and balance.

Alternatives

Python properties (`@property` decorator) provide a more Pythonic way to implement getters and setters, allowing you to control attribute access while maintaining a clean syntax.

Pros

  • Improved data integrity.
  • Reduced coupling between classes.
  • Easier code maintenance.
  • Increased code reusability.

Cons

  • Can add a small overhead due to getter and setter methods (minimal in Python).
  • Requires careful planning of access control.
  • Can make code slightly more verbose.

FAQ

  • Why doesn't Python have true private access modifiers?

    Python's philosophy emphasizes readability and ease of use. It's assumed that developers will follow the naming conventions to respect the intended visibility of class members. The lack of strict enforcement encourages a more collaborative and less restrictive coding style. It's often said that 'we are all consenting adults here'.
  • Can I still access 'private' attributes in Python?

    Yes, you can access 'private' attributes using name mangling (e.g., `object._ClassName__attribute`). However, it's strongly discouraged to do so as it violates the intended encapsulation and can lead to unexpected behavior.
  • What is name mangling?

    Name mangling is a process in which Python renames 'private' attributes (those with a double leading underscore) to avoid naming conflicts in subclasses. The name is changed to `_ClassName__attribute`.