Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

🟢 Beginner🔵 Advanced
Classes & ObjectsMethods & SelfInstance VariablesClass VariablesConstructors & InitializationInheritance BasicsPolymorphismEncapsulationMagic Methods & Dunder
Python/Oop/Encapsulation

🔐 Encapsulation — Advanced Data Protection and Privacy

Advanced encapsulation includes name mangling for true privacy, sophisticated property patterns, computed attributes, and validation chains. These techniques create robust, maintainable object models.


🎯 Name Mangling for Privacy

Double underscore prefix triggers name mangling, providing stronger privacy than single underscore convention.

class BankAccount:
    def __init__(self, owner, balance):
        self.__owner = owner      # Name mangled
        self.__balance = balance  # Name mangled
        self._pin = "1234"        # Private convention

    def __private_method(self):
        """Truly private method"""
        return f"Balance: {self.__balance}"

    def get_balance(self, pin):
        if pin == self._pin:
            return self.__balance
        raise ValueError("Invalid PIN")

account = BankAccount("Alice", 1000)

# Can't access mangled names directly
try:
    print(account.__balance)  # Error!
except AttributeError as e:
    print(f"Error: {e}")

# Name mangled to _BankAccount__balance
print(account._BankAccount__balance)  # Possible but strongly discouraged

# Use public method
print(account.get_balance("1234"))

🔄 Property Chains and Validation

Create sophisticated property validation chains.

class Email:
    def __init__(self, value=None):
        self._value = None
        if value:
            self.value = value

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, v):
        if not self._is_valid(v):
            raise ValueError(f"Invalid email: {v}")
        self._value = v

    @staticmethod
    def _is_valid(email):
        return "@" in email and "." in email.split("@")[1]

class User:
    def __init__(self, name, email):
        self.name = name
        self.__email = Email()
        self.email = email  # Calls setter

    @property
    def email(self):
        return self.__email.value

    @email.setter
    def email(self, value):
        self.__email.value = value

user = User("Alice", "alice@example.com")
print(user.email)

try:
    user.email = "invalid-email"
except ValueError as e:
    print(f"Error: {e}")

📊 Cached Properties

Cache computed properties for performance while maintaining lazy initialization.

class CachedProperty:
    def __init__(self, func):
        self.func = func
        self.name = func.__name__

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        cache_name = f"_cached_{self.name}"
        if not hasattr(obj, cache_name):
            setattr(obj, cache_name, self.func(obj))
        return getattr(obj, cache_name)

class DataAnalyzer:
    def __init__(self, data):
        self.data = data

    @CachedProperty
    def mean(self):
        print("Computing mean...")
        return sum(self.data) / len(self.data)

    @CachedProperty
    def median(self):
        print("Computing median...")
        sorted_data = sorted(self.data)
        n = len(sorted_data)
        return sorted_data[n//2]

analyzer = DataAnalyzer([1, 2, 3, 4, 5])
print(f"Mean: {analyzer.mean}")
print(f"Mean again: {analyzer.mean}")  # No recomputation
print(f"Median: {analyzer.median}")

🔐 Sealed Objects

Create immutable or sealed objects that can't be modified after creation.

class Sealed:
    """Base class for sealed objects"""
    __slots__ = ()

    def __setattr__(self, name, value):
        if hasattr(self, '__dict__'):
            raise AttributeError("Cannot modify sealed object")
        super().__setattr__(name, value)

class Point(Sealed):
    __slots__ = ('_x', '_y')

    def __init__(self, x, y):
        object.__setattr__(self, '_x', x)
        object.__setattr__(self, '_y', y)

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

point = Point(10, 20)
print(f"Point: ({point.x}, {point.y})")

try:
    point.x = 30  # Error!
except AttributeError as e:
    print(f"Error: {e}")

📚 Real-World Examples

Encryption at Property Level

import hashlib

class SecureString:
    def __init__(self, value=None):
        self._encrypted = None
        if value:
            self.value = value

    @property
    def value(self):
        if self._encrypted:
            return f"[ENCRYPTED - {len(self._encrypted)} chars]"
        return None

    @value.setter
    def value(self, plaintext):
        self._encrypted = hashlib.sha256(plaintext.encode()).hexdigest()

    def verify(self, plaintext):
        return self._encrypted == hashlib.sha256(plaintext.encode()).hexdigest()

class CredentialManager:
    def __init__(self):
        self.__password = SecureString()

    def set_password(self, password):
        self.__password.value = password

    def verify_password(self, password):
        return self.__password.verify(password)

    def get_password_info(self):
        return self.__password.value

manager = CredentialManager()
manager.set_password("super_secret_123")
print(manager.get_password_info())
print(f"Correct? {manager.verify_password('super_secret_123')}")
print(f"Wrong? {manager.verify_password('wrong_password')}")

Configuration with Immutable Values

class ImmutableConfig:
    def __init__(self, **kwargs):
        self.__data = kwargs
        self.__frozen = True

    def __getattr__(self, name):
        if name.startswith('_'):
            return object.__getattribute__(self, name)
        if name in self.__data:
            return self.__data[name]
        raise AttributeError(f"No config: {name}")

    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)
        elif hasattr(self, '_ImmutableConfig__frozen'):
            raise AttributeError("Config is immutable")
        else:
            super().__setattr__(name, value)

config = ImmutableConfig(
    debug=True,
    timeout=30,
    max_retries=3
)

print(config.debug)
print(config.timeout)

try:
    config.debug = False  # Error!
except AttributeError as e:
    print(f"Error: {e}")

✅ Key Takeaways

ConceptRemember
Name ManglingDouble underscore for stronger privacy
Property ChainsCompose validation and access control
Cached PropertiesCache expensive computations
Sealed ObjectsPrevent modification after creation
EncryptionProtect sensitive data at property level
ImmutabilityCreate unchangeable objects
Access ControlMulti-level privacy (single _, double __, property)
RobustnessEnforce invariants at the object level

🔗 What's Next?

Finish with advanced magic methods patterns.

Advanced: Magic Methods & Dunder →


Ready to practice? Try challenges or explore more concepts


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn