Object-Oriented Programming (OOP) is a design paradigm that structures software around state (data attributes) and behaviors (methods). Python is a multi-paradigm language where everything—including numbers, strings, functions, and modules—is an instance of a class.
1. Procedural vs. Object-Oriented Programming
Procedural programming organizes code around sequential functions operating on decoupled data structures. OOP binds state and behavior into self-contained objects, enhancing modularity and maintainability.
| Dimension | Procedural Programming | Object-Oriented Programming |
|---|---|---|
| Primary Unit | Functions operating on external data structures. | Objects encapsulating state and methods. |
| Data Access | Global or mutable state passed across functions. | Encapsulated access via controlled interfaces. |
| Extensibility | Modifying existing procedures across modules. | Extending classes through inheritance and composition. |
2. The Four Core Pillars of OOP
A. Encapsulation & Name Mangling
Encapsulation restricts direct access to an object's internal state. Python uses naming conventions to signal visibility: a single underscore (_protected) signals internal use, while a double underscore (__private) triggers name mangling to transform the attribute name to _ClassName__attribute.
B. Abstraction via Abstract Base Classes (ABC)
Abstraction hides implementation details behind unified contracts. Using Python's abc module, classes inheriting from ABC with @abstractmethod force subclasses to implement specific interfaces before instantiation.
C. Inheritance
Inheritance lets child classes acquire attributes and methods from parent classes, promoting code reuse. Python uses Method Resolution Order (MRO) with C3 Linearization to resolve calls across complex inheritance chains.
D. Polymorphism & Duck Typing
Polymorphism lets different classes handle the same method interface differently. Python relies heavily on Duck Typing: "If it walks like a duck and quacks like a duck, it's a duck." Execution depends on method presence rather than class inheritance.
3. Composition vs. Inheritance
Inheritance enforces an "Is-A" relationship, while composition represents a "Has-A" relationship. Design principles advocate for "Composition over Inheritance" to minimize brittle class hierarchies and create flexible system architectures.
4. High-Level SOLID Design Principles
- Single Responsibility (SRP): A class should have one reason to change.
- Open/Closed (OCP): Software entities should be open for extension, but closed for modification.
- Liskov Substitution (LSP): Subtypes must be substitutable for their base types without altering correctness.
- Interface Segregation (ISP): Clients should not be forced to depend on methods they do not use.
- Dependency Inversion (DIP): Depend upon abstractions (ABCs), not concrete implementations.
🏋️ Try It Yourself: Advanced Challenges
Construct a `CryptoWallet` class featuring private `__private_key` and `__balance` attributes. Expose explicit balance query methods while verifying name mangling in the attribute dictionary.
Create an Abstract Base Class `PaymentGateway` with abstract methods `authorize()` and `process()`. Implement concrete subclasses `StripeGateway` and `PayPalGateway`.
Create three distinct classes (`EmailSender`, `SMSSender`, `WebhookSender`) implementing a `send_payload(msg)` method without shared inheritance, and execute them via a single processing function.
Design a `Computer` class composed of distinct `CPU`, `RAM`, and `Storage` class instances, triggering startup diagnostics sequentially.