Tutorials Logic, IN info@tutorialslogic.com

Encapsulation in Python Private Attributes

Encapsulation Basics

Encapsulation means keeping object data behind clear methods or properties instead of changing it freely everywhere.

Python uses naming conventions and properties to signal which values are public and which details should stay internal.

The goal is not hiding code from other developers. The goal is protecting rules, validation, and object consistency.

Python Encapsulation

Encapsulation bundles data (attributes) and the methods that operate on that data into a single unit (class), and restricts direct access to some components. It protects the internal state of an object from unintended modification.

Access Modifiers

Python uses naming conventions rather than strict keywords like private or protected.

Convention Example Meaning
No prefix self.name Public - accessible anywhere
Single underscore _ self._salary Protected - "don't touch from outside" (convention only)
Double underscore __ self.__password Private - name-mangled, harder to access from outside

Use Naming Rules for Access

Use Naming Rules for Access
class BankAccount:
    def __init__(self, owner: str, balance: float):
        self.owner = owner          # public
        self._account_type = "savings"  # protected (convention)
        self.__balance = balance    # private (name-mangled)

    def deposit(self, amount: float):
        if amount > 0:
            self.__balance += amount

    def get_balance(self) -> float:
        return self.__balance       # controlled access

account = BankAccount("Alice", 1000)

print(account.owner)           # Alice  (public - fine)
print(account._account_type)   # savings (works but discouraged)
print(account.get_balance())   # 1000   (via method - correct way)

# Direct access to __balance fails
# print(account.__balance)     # AttributeError!

# Name mangling - Python renames it to _ClassName__attr
print(account._BankAccount__balance)  # 1000 (possible but bad practice)

Getters and Setters

@property Decorator

@property Decorator
class Temperature:
    def __init__(self, celsius: float = 0):
        self.celsius = celsius

    @property
    def celsius(self) -> float:
        """Return the stored Celsius value."""
        return self._celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        """Store a physically valid Celsius value."""
        if value < -273.15:
            raise ValueError("temperature is below absolute zero")
        self._celsius = value

    @property
    def fahrenheit(self) -> float:
        return self._celsius * 9 / 5 + 32

temperature = Temperature(25)
print(temperature.celsius)
print(temperature.fahrenheit)
Output
25
77.0

Encapsulation Example

Bank Account

Bank Account
class BankAccount:
    def __init__(self, owner: str, initial_balance: float = 0):
        self.__owner = owner
        self.__balance = initial_balance
        self.__transactions = []

    @property
    def owner(self) -> str:
        return self.__owner

    @property
    def balance(self) -> float:
        return self.__balance

    def deposit(self, amount: float) -> None:
        if amount <= 0:
            raise ValueError("Deposit amount must be positive")
        self.__balance += amount
        self.__transactions.append(f"+${amount:.2f}")

    def withdraw(self, amount: float) -> None:
        if amount <= 0:
            raise ValueError("Withdrawal amount must be positive")
        if amount > self.__balance:
            raise ValueError("Insufficient funds")
        self.__balance -= amount
        self.__transactions.append(f"-${amount:.2f}")

    def get_statement(self) -> str:
        history = "\n".join(self.__transactions) or "No transactions"
        return f"Account: {self.__owner}\nBalance: ${self.__balance:.2f}\n{history}"

acc = BankAccount("Alice", 500)
acc.deposit(200)
acc.withdraw(100)
print(acc.get_statement())
# Account: Alice
# Balance: $600.00
# +$200.00
# -$100.00
Encapsulation readiness check

Can You Protect Object State?

5 checks
  • No prefix means self.name; a typical example is Public - accessible anywhere.
  • Single underscore _ means self._salary; a typical example is Protected - "don't touch from outside" (convention only).
  • Double underscore __ means self.__password; a typical example is Private - name-mangled, harder to access from outside.
  • Access Modifiers in Python includes self.name, self._salary, and self.__password.
  • Encapsulation bundles data (attributes) and the methods that operate on that data into a single unit (class), and restricts direct access to some components.

State Protection Traps

  • Hiding every attribute without a reason

    Protect data when you need validation or controlled changes, not as decoration.
  • Using getters and setters mechanically

    Use direct attributes for simple public data; add properties when rules are needed.
  • Letting outside code break object rules

    Put validation inside methods that change important state.

Try this next

Protect an Object Value

0 of 3 completed

  1. Create deposit and withdraw methods that reject invalid amounts.
  2. Use a property to return a formatted display name from first and last name.
  3. Decide which fields in a User class can be public and which need validation.

Questions About Encapsulation

Python uses conventions such as _name and name mangling with __name, but it relies on developer discipline more than strict privacy.

Use property when reading an attribute should look simple, but setting it needs validation or computed behavior.

Direct changes can skip validation. Methods and properties keep the object in a valid state.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.