Python Object-Oriented Programming Fundamentals

Object-Oriented Programming (OOP) represents a fundamental programming paradigm where code is structured around objects rather than functions. An object combines data (attributes) and functions (methods) that operate on that data into a single unit.

Procedural vs. Object-Oriented Approach

Consider managing student records. Using a procedural style, each student can be represented as a dictionary:

student_a = {'name': 'Alice', 'score': 92}
student_b = {'name': 'Charlie', 'score': 85}

def display_result(std):
    print(f"{std['name']}: {std['score']}")

With an object-oriented approach, the focus shifts to designing a Student type that encapsulates both the data and the behavior for working with student information:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def display(self):
        print(f"{self.name}:{self.score}")


alex = Student("Alex", 72)
nina = Student("Nina", 95)

alex.display()
nina.display()

Output:

Alex:72
Nina:95

This design mirrors real-world concepts. A class defines the blueprint—a Student represents the abstract notion of a student. An instance represents a specific student, like alex or nina. Classes bundle data and methods together, providing a higher level of abstraction than functions alone.

Key OOP Terminology

  • Class: A template defining a collection of objects with shared attributes and behaviors
  • Method: A function defined within a class
  • Class Variable: A variable shared across all instances of a class
  • Instance Variable: A variable bound to a specific instance
  • Inheritance: A mechanism where a subclass derives properties and methods from a parent class
  • Polymorphism: The ability for different classes to respond to the same method call in their own way
  • Instantiation: The process of creating an object from a class

Defining and Instantiating Classes

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        print(f"{self.name} is {self.age} years old")


marcus = Person("Marcus", 28)
print(marcus.name)  # Marcus
print(marcus.age)   # 28

The __init__ method serves as the constructor. Its first parameter is always self, which references the instance being created. By convention, any attribute prefixed with self. becomes an instance variable accessible throughout the class.

Method Behavior

Methods are functions bound to objects. Unlike standalone functions, methods have implicit access to instance data through self. The syntax for parameters, default values, and argument unpacking works identically to regular functions.

Encapsulation

Encapsulation hides internal implementation details while exposing a clean interface. Users of a class interact with methods without needing to understand how those methods are implemented internally.

Controlling Access

Python uses naming conventions to indicate access levels. Prefix an attrbiute with __ (double underscore) to make it private:

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # private attribute

    def get_balance(self):
        return self.__balance


account = BankAccount(1000)
print(account.get_balance())  # 1000
# account.__balance  # AttributeError

By convention, private attributes signal that external code should not directly modify them.

Inheritance

Inheritance enables code reuse and hierarchical organization:

class Animal:
    def move(self):
        print("Animal is moving...")


class Dog(Animal):
    pass


class Cat(Animal):
    def move(self):
        print("Cat is moving...")


buddy = Dog()
buddy.move()  # Animal is moving...

whiskers = Cat()
whiskers.move()  # Cat is moving...

When a subclass defines the same method as its parent, the subclass version overrides the parent's—this is method overriding, which enables polymorphism.

Type Checking and Compatibility

All classes implicitly inherit from object. This means custom types behave identically to built-in types:

numbers = list()      # list type
a Creature = Animal()  # Animal type
pet = Dog()            # Dog type

print(isinstance(numbers, list))   # True
print(isinstance(pet, Animal))      # True
print(isinstance(pet, Dog))        # True

A Dog instance satisfies both Dog and Animal type checks because inheritance creates an "is-a" relationship. However, the reverse is not true:

base = Animal()
print(isinstance(base, Dog))  # False

This follows the logical principle that a Dog is an Animal, but not all Animals are Dogs.

Polymorphism in Practice

class Animal:
    def move(self):
        print("Animal is moving...")


class Rabbit(Animal):
    def move(self):
        print("Rabbit hops along...")


class Bird(Animal):
    def move(self):
        print("Bird soars through the sky...")


def perform_action(entity):
    entity.move()
    entity.move()


perform_action(Animal())
perform_action(Rabbit())
perform_action(Bird())

This demonstrates polymorphism: the perform_action function works with any type that has a move method, without needing to know the specific type. Adding new animal types requires no modifications to exitsing functions—the "open-closed" principle: open for extension, closed for modification.

Duck Typing

Python's dynamic nature enables duck typing. Unlike statically-typed languages requiring explicit inheritance, any object with the required method can be used:

class Stopwatch:
    def move(self):
        print("Timer started")


perform_action(Stopwatch())  # Works without Animal inheritance

If it walks like a duck and quacks like a duck, it's treated as a duck. This flexibility removes the necessity of strict inheritance hierarchies.

Method Resolution Order

Python supports multiple inheritance, where a class can inherit from multiple parent classes. The method resolution order determines which parent class's method is invoked when conflicts occur.

Tags: python object-oriented OOP Classes Inheritance

Posted on Wed, 21 Oct 2026 16:46:11 +0000 by mook