Core Principles of Object-Oriented Programming

Encapsulation

Encapsulation refers to the bundling of data and methods within a single unit, typically a class. This concept involves hiding internal details from external access, ensuring that certain attributes or functions remain inaccessible outside their defined scope.

The primary motivation for encapsulation is to protect sensitive information by restricting direct access to internal components. This approach enhances security and prevents unintended interference with the object's state.

Inheritance

Inheritance enables the creation of new classes based on existing ones. The newly created class is known as a subclass or derived class, while the original is referred to as a superclass or base class. A subclass inherits all public and protected members of its parent class.

Inheritance addresses redundancy among classes rather than objects, allowing shared functionality to be defined once in a base class and reused across multiple derived classes.

In Python 2, classes are classified into classic and new-style depending on whether they inherit from object. However, Python 3 treats all classes as new-style by default, eliminating the distinction between classic and new-style classes.

The method resolution order (MRO) determines how methods are looked up when invoked. Python uses the C3 linearization algorithm to compute MRO, which ensures consistent behavior according to three rules:

  1. Subclasses are checked before their parents
  2. Parents are checked in the order listed in the inheritance tuple
  3. If multiple valid choices exist, the first parent is selected

When performing attribute lookup, Python follows the MRO sequence starting from the instance, then through the class hierarchy.

Single Inheritance

Single inheritance occurs when a class inherits from exactly one parenet class:

class Base:
    pass

class Derived(Base):
    pass

Attribute resolution in single inheritance proceeds in this order:

  1. Instance namespace
  2. Class namespace of the instance’s class
  3. Parent class namespace

Multiple Inheritance

Multiple inheritance allows a class to inherit from more than one parent class:

class Parent1:
    pass

class Parent2:
    pass

class Child(Parent1, Parent2):
    pass

In Python 2, classic classes followed a depth-first search pattern for attribute resolution. In contrast, Python 3 uses breadth-first search with new-style classes.

For non-diamond inheritance patterns, the MRO follows a predictable sequence. For example:

class E:
    def method(self):
        print('E')

class F:
    def method(self):
        print('F')

class B(E):
    def method(self):
        print('B')

class C(F):
    def method(self):
        print('C')

class D:
    def method(self):
        print('D')

class A(B, C, D):
    pass

print(A.mro())
# Output: [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]

Super and MRO Usage

The super() function provides access to inherited methods from a parent class. It respects the MRO and ensures correct method resolution:

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

class Chinese(Parent):
    def __init__(self, name, age, gender, money):
        super().__init__(name, age, gender)
        self.money = money

    def speak_english(self):
        print(f'{self.name} has {self.money} RMB')

person = Chinese('qwe', 12, 'male', '1233')
person.speak_english()

To inspect the MRO of a class:

class A:
    def method(self):
        super().method()

class B:
    def method(self):
        print('B')

class C(A, B):
    pass

print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

Polymorphism

Polymorphism means that a single interface can represent different types. It allows objects of different types to be treated uniformly through a common interface.

Abstract classes define contracts that subclasses must fulfill:

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def speak(self):
        pass

    @abc.abstractmethod
    def sound(self):
        pass

# This would raise an error if instantiated
# animal = Animal()

The duck typing principle emphasizes behavior over type. Any object implementing required methods can be used interchangeably:

class Person(Animal):
    def speak(self):
        pass
    def sound(self):
        pass

class Dog(Animal):
    def speak(self):
        pass
    def sound(self):
        pass

class Pig(Animal):
    def speak(self):
        pass
    def sound(self):
        pass

# Polymorphic function
def make_animal_speak(animal):
    return animal.speak()

# Works with any animal-like object
make_animal_speak(Person())
make_animal_speak(Dog())
make_animal_speak(Pig())

Derivation

Derived classes can introduce new attributes and override parent methods. Attribute lookup prioritizes child attributes over parent ones:

class Base:
    planet = 'Earth'
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Chinese(Base):
    nation = 'China'
    def __init__(self, name, age, gender, money):
        super().__init__(name, age, gender)
        self.money = money

    def speak_english(self):
        print(f'{self.name} has {self.money} RMB')

person = Chinese('qwe', 12, 'male', '1233')
person.speak_english()

Using super() ensures proper method chaining through the MRO. While both explicit parent calls and super() achieve similar results, the latter is preferred in modern Python due to better maintainability and support for complex inheritance hierarchies.

Tags: Object-Oriented Programming encapsulation Inheritance Polymorphism python

Posted on Tue, 03 Nov 2026 16:49:41 +0000 by Seamless