Welcome to Day 2! Today we focus on two core pillars of Object-Oriented Programming: Encapsulation: Hiding internal state and requiring all interaction to go through performing validation or controlled methods. Abstraction: Hiding complex implementation details and exposing only a clean, simplified interface to the user. 1. Access Modifiers: Public, Protected, & Private 🛡️ Python does not have strict enforcement keywords like public or private found in Java or C++. Instead, it uses naming conventions and name mangling to communicate intent and protect internal data. ┌─────────────────────────────────────────────────────────────────────────────┐ │ ACCESS MODIFIERS IN PYTHON │ ├──────────────┬───────────────┬──────────────────────────────────────────────┤ │ Level │ Syntax │ Scope / Intended Access │ ├──────────────┼───────────────┼──────────────────────────────────────────────┤ │ Public │ self.name │ Accessible anywhere (inside & outside class) │ │ Protected │ self._balance │ Internal & subclasses only (Convention) │ │ Private │ self.__pin │ Class internal only (Triggers Name Mangling) │ └──────────────┴───────────────┴──────────────────────────────────────────────┘ Enter fullscreen mode Exit fullscreen mode Public (self.name): Fully accessible from anywhere. Protected (self._balance): Single leading underscore. Signals to developers: "This is internal—do not mutate or access outside this class or its subclasses." Private (self.__pin): Double leading underscore. Triggers name mangling (_ClassName__attribute), making it hard to accidentally access or override outside the class. 💻 Code Example: Access Modifiers & Name Mangling class SecureVault: def __init__(self, owner: str, balance: float, pin_code: str): self.owner = owner # Public self._balance = balance # Protected (convention) self.__pin = pin_code # Private (name mangled) def verify_pin(self, pin: str) -> bool: return self.__pin == pin vault = SecureVault("Alice", 50000.0, "9876") # Public access works as expected print(vault.owner) # Output: Alice # Protected access works, but violates Python conventions print(vault._balance) # Output: 50000.0 # Private access raises an AttributeError directly try: print(vault.__pin) except AttributeError as e: print("Direct private access blocked:", e) # Accessing private attribute via Python's name-mangled name print("Mangled PIN access:", vault._SecureVault__pin) # Output: 9876 Enter fullscreen mode Exit fullscreen mode 2. Getters & Setters (@property) ⚙️ Instead of writing verbose Java-style methods like get_balance() and set_balance(), Python provides the @property decorator. It allows you to access methods like regular attributes while giving you full control over validation, read-only constraints, and dynamic computation. GETTER SETTER user.balance ───────► [@property] ───────► Returns internal self._balance │ user.balance = 100 ────────┴──────────────► [@balance.setter] ──► Validates > 0 Enter fullscreen mode Exit fullscreen mode 💻 Code Example: Controlled Access with @property class BankAccount: def __init__(self, owner: str, initial_balance: float): self.owner = owner self._balance = initial_balance # Internal state # Getter: Exposes balance as a read-only property @property def balance(self) -> float: return round(self._balance, 2) # Setter: Validates new values before updating balance @balance.setter def balance(self, value: float) -> None: if value Dict[str, Any]: """Processes a payment transaction. Must be overridden by subclasses.""" pass @abstractmethod def refund_payment(self, transaction_id: str, amount: float) -> bool: """Refunds a previous transaction. Must be overridden by subclasses.""" pass # ========================================== # 2. CONCRETE IMPLEMENTATION: CREDIT CARD # ========================================== class CreditCardProcessor(PaymentProcessor): def __init__(self, merchant_id: str, api_key: str): super().__init__("Credit Card Gateway") self.merchant_id = merchant_id self.__api_key = api_key # Private API key def process_payment(self, amount: float) -> Dict[str, Any]: if amount bool: print(f"[{self.provider_name}] Refunding ${amount:.2f} on Transaction #{transaction_id}...") return True # ========================================== # 3. CONCRETE IMPLEMENTATION: PAYPAL # ========================================== class PayPalProcessor(PaymentProcessor): def __init__(self, client_email: str): super().__init__("PayPal System") self._client_email = client_email # Protected attribute def process_payment(self, amount: float) -> Dict[str, Any]: if amount bool: print(f"[{self.provider_name}] Issuing PayPal refund for ${amount:.2f} to {self._client_email}...") return True # ========================================== # 4. UNIFIED PAYMENT SERVICE (ABSTRACTION IN ACTION) # ========================================== class CheckoutService: """Consumes any PaymentProcessor through abstraction without knowing inner mechanics.""" def __init__(self, processor: PaymentProcessor): self.processor = processor def checkout(self, amount: float) -> None: print("\n--- Initiating Checkout ---") result = self.processor.process_payment(amount) if result["success"]: print(f"✓ Checkout Successful! Txn ID: {result['transaction_id']}") else: print(f"✗ Checkout Failed: {result.get('error')}") # ========================================== # TEST EXECUTION SUITE # ========================================== if __name__ == "__main__": # Attempting to instantiate abstract class raises TypeError try: base = PaymentProcessor("Generic") except TypeError as err: print("Abstract Class Guard Working:") print(f" --> {err}\n") # Instantiate concrete processors cc_gateway = CreditCardProcessor(merchant_id="MERCH-8821", api_key="secret_live_key") paypal_gateway = PayPalProcessor(client_email="billing@corporate.org") # Process transactions dynamically via CheckoutService cart_1 = CheckoutService(cc_gateway) cart_1.checkout(149.99) cart_2 = CheckoutService(paypal_gateway) cart_2.checkout(89.50) # Trigger a refund cc_gateway.refund_payment("CC-9A2F1B0C", 149.99) Enter fullscreen mode Exit fullscreen mode Step 3: Run & Verify uv run payment_system.py Enter fullscreen mode Exit fullscreen mode Output Summary Abstract Class Guard Working: --> Can't instantiate abstract class PaymentProcessor without an implementation for abstract methods 'process_payment', 'refund_payment' --- Initiating Checkout --- [Credit Card Gateway] Processing $149.99 via Merchant ID: MERCH-8821... ✓ Checkout Successful! Txn ID: CC-7B9F10A2 --- Initiating Checkout --- [PayPal System] Charging $89.50 to account billing@corporate.org... ✓ Checkout Successful! Txn ID: PP-3E41D8B9 [Credit Card Gateway] Refunding $149.99 on Transaction #CC-9A2F1B0C... Enter fullscreen mode Exit fullscreen mode
#02 – Encapsulation & Abstraction in Python
Full Article
Original Source
Read the full article at Dev →KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.