Object-Oriented Programming
Modeling Real-World Objects with OOP
Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around data, or "objects," rather than functions and logic. An object can be defined as a data field that has unique attributes and behavior.
Classes and Objects
A class is a blueprint for creating objects. It defines a set of attributes (variables) and methods (functions) that the created objects will have. An object is an instance of a class.
The __init__()
method is a special method, often called a constructor, that is automatically run whenever a new object is created from the class. The self
parameter is a reference to the current instance of the class and is used to access variables that belong to the class.
class Dog: # Class attribute (shared by all instances) species = "Canis familiaris"
# The __init__() method is a constructor def __init__(self, name, age): # Instance attributes (unique to each instance) self.name = name self.age = age
# Create object instancesdog1 = Dog("Buddy", 5)dog2 = Dog("Lucy", 3)
# Each instance has its own dataprint(f"{dog1.name} is {dog1.age} years old.") # Buddy is 5 years old.print(f"{dog2.name} is {dog2.age} years old.") # Lucy is 3 years old.
# But they share the class attributeprint(f"They are both of the species: {Dog.species}")
Memory and Attributes: A Visual Guide
Understanding how memory is organized for classes and objects is key. Class attributes are stored once with the class definition, and all instances share them. Instance attributes are created for each unique object, so every object gets its own copy.
Class: Dog
- species: "Canis familiaris"
Object 1 (dog1)
- name: "Buddy"
- age: 5
Object 2 (dog2)
- name: "Lucy"
- age: 3
The Four Pillars of OOP
- Inheritance: Allows us to define a class that inherits all the methods and properties from another class. This promotes code reuse.
class Animal: # Parent class def __init__(self, name): self.name = name def speak(self): return "Some generic animal sound"
class Cat(Animal): # Child class def speak(self): # Method overriding return "Meow" my_cat = Cat("Whiskers") print(my_cat.speak()) # Outputs "Meow"
- Encapsulation: The bundling of data and the methods that operate on that data into a single unit (a class). It restricts direct access to some of an object's components. Python uses a convention of prefixing an attribute with an underscore (e.g.,
_balance
) to indicate it's intended for internal use.class BankAccount: def init(self, initial_amount): self._balance = initial_amount # By convention, _ means "private"
def deposit(self, amount): if amount > 0: self._balance += amount
def get_balance(self): return self._balance # Provide controlled access
- Polymorphism: This means "many forms" and allows objects of different classes to be treated as objects of a common superclass. It's the ability to use a single interface for different data types.
class Dog(Animal): def speak(self): return "Woof" class Cat(Animal): def speak(self): return "Meow"
The function works for any object that has a speak() method.
def make_animal_speak(animal): print(animal.speak())
make_animal_speak(Dog("Buddy")) # Prints "Woof" make_animal_speak(Cat("Lucy")) # Prints "Meow"
- Abstraction: Hiding complex implementation details and showing only the necessary features of the object. An "abstract class" can define methods that must be implemented by its subclasses, creating a contract.
from abc import ABC, abstractmethod
class Shape(ABC): # Abstract Base Class @abstractmethod def area(self): pass # Subclasses MUST implement this
class Square(Shape): def init(self, side): self.side = side def area(self): return self.side * self.side
You cannot create an object of an abstract class:
my_shape = Shape() # This would raise a TypeError