Types of Inheritance in Python
Types of Inheritance in Python Interview Questions
What are the different types of inheritance supported in Python?
Python supports 5 main types of inheritance:
1. Single Inheritance
2. Multiple Inheritance
3. Multilevel Inheritance
4. Hierarchical Inheritance
5. Hybrid Inheritance
Python also supports Cyclic Inheritance (not recommended) and doesn't support Private Inheritance like C++.
What is Single Inheritance with example?
Single Inheritance: A class inherits from only one parent class.
class Parent:
def parent_method(self):
return "Parent method"
class Child(Parent): # Single inheritance def child_method(self): return "Child method"
obj = Child() print(obj.parent_method()) # Inherited from Parent print(obj.child_method()) # Own method
class Child(Parent): # Single inheritance def child_method(self): return "Child method"
obj = Child() print(obj.parent_method()) # Inherited from Parent print(obj.child_method()) # Own method
Structure:
Parent
↑
Child
Parent
↑
Child
What is Multiple Inheritance with example?
Multiple Inheritance: A class inherits from more than one parent class.
class Father:
def father_method(self):
return "Father's method"
class Mother: def mother_method(self): return "Mother's method"
class Child(Father, Mother): # Multiple inheritance def child_method(self): return "Child's method"
obj = Child() print(obj.father_method()) # From Father print(obj.mother_method()) # From Mother print(obj.child_method()) # Own method
class Mother: def mother_method(self): return "Mother's method"
class Child(Father, Mother): # Multiple inheritance def child_method(self): return "Child's method"
obj = Child() print(obj.father_method()) # From Father print(obj.mother_method()) # From Mother print(obj.child_method()) # Own method
Diamond Problem: Multiple inheritance can cause ambiguity if parents have same method names. Python uses MRO to resolve this.
What is Multilevel Inheritance with example?
Multilevel Inheritance: Chain of inheritance where a class inherits from a child class.
class GrandParent:
def grandparent_method(self):
return "GrandParent method"
class Parent(GrandParent): # Level 1 def parent_method(self): return "Parent method"
class Child(Parent): # Level 2 (Multilevel) def child_method(self): return "Child method"
obj = Child() print(obj.grandparent_method()) # From GrandParent print(obj.parent_method()) # From Parent print(obj.child_method()) # Own method
class Parent(GrandParent): # Level 1 def parent_method(self): return "Parent method"
class Child(Parent): # Level 2 (Multilevel) def child_method(self): return "Child method"
obj = Child() print(obj.grandparent_method()) # From GrandParent print(obj.parent_method()) # From Parent print(obj.child_method()) # Own method
Structure:
GrandParent
↑
Parent
↑
Child
GrandParent
↑
Parent
↑
Child
What is Hierarchical Inheritance with example?
Hierarchical Inheritance: Multiple child classes inherit from a single parent class.
class Animal: # Parent class
def eat(self):
return "Eating..."
class Dog(Animal): # Child 1 def bark(self): return "Barking..."
class Cat(Animal): # Child 2 def meow(self): return "Meowing..."
class Cow(Animal): # Child 3 def moo(self): return "Mooing..."
dog = Dog() cat = Cat() print(dog.eat()) # Inherited from Animal print(cat.eat()) # Inherited from Animal
class Dog(Animal): # Child 1 def bark(self): return "Barking..."
class Cat(Animal): # Child 2 def meow(self): return "Meowing..."
class Cow(Animal): # Child 3 def moo(self): return "Mooing..."
dog = Dog() cat = Cat() print(dog.eat()) # Inherited from Animal print(cat.eat()) # Inherited from Animal
Structure:
Animal
↙ ↓ ↘
Dog Cat Cow
Animal
↙ ↓ ↘
Dog Cat Cow
What is Hybrid Inheritance with example?
Hybrid Inheritance: Combination of two or more types of inheritance.
class A:
def method_a(self):
return "Method A"
class B(A): # Single inheritance def method_b(self): return "Method B"
class C(A): # Hierarchical (with B) def method_c(self): return "Method C"
class D(B, C): # Multiple inheritance + Hierarchical def method_d(self): return "Method D"
obj = D() print(obj.method_a()) # From A (via B or C) print(obj.method_b()) # From B print(obj.method_c()) # From C print(obj.method_d()) # Own method
class B(A): # Single inheritance def method_b(self): return "Method B"
class C(A): # Hierarchical (with B) def method_c(self): return "Method C"
class D(B, C): # Multiple inheritance + Hierarchical def method_d(self): return "Method D"
obj = D() print(obj.method_a()) # From A (via B or C) print(obj.method_b()) # From B print(obj.method_c()) # From C print(obj.method_d()) # Own method
Hybrid inheritance often creates the Diamond Problem, resolved by Python's MRO.
What is Method Resolution Order (MRO) in Python inheritance?
MRO determines the order in which base classes are searched when looking for a method. Python uses C3 Linearization algorithm.
class A:
def method(self):
return "A"
class B(A): def method(self): return "B"
class C(A): def method(self): return "C"
class D(B, C): pass
print(D.mro()) # [D, B, C, A, object] obj = D() print(obj.method()) # "B" (follows MRO: D -> B -> C -> A)
Check MRO using: ClassName.mro() or ClassName.__mro__
class B(A): def method(self): return "B"
class C(A): def method(self): return "C"
class D(B, C): pass
print(D.mro()) # [D, B, C, A, object] obj = D() print(obj.method()) # "B" (follows MRO: D -> B -> C -> A)
What is the Diamond Problem in Multiple Inheritance?
Diamond Problem occurs when a class inherits from two classes that both inherit from the same base class, creating ambiguity.
class A:
def method(self):
return "A"
class B(A): def method(self): return "B"
class C(A): def method(self): return "C"
class D(B, C): # Diamond shape: D → B → A ← C pass
obj = D() print(obj.method()) # Which method? B or C? # Python resolves using MRO: D -> B -> C -> A # So output is "B"
class B(A): def method(self): return "B"
class C(A): def method(self): return "C"
class D(B, C): # Diamond shape: D → B → A ← C pass
obj = D() print(obj.method()) # Which method? B or C? # Python resolves using MRO: D -> B -> C -> A # So output is "B"
Diamond Problem Structure:
A
↙ ↘
B C
↘ ↙
D
A
↙ ↘
B C
↘ ↙
D
How does Python handle constructor calls in different inheritance types?
Use super() to call parent constructors. super() follows MRO.
class A:
def __init__(self):
print("A init")
class B(A): def __init__(self): super().__init__() # Calls A.__init__ print("B init")
class C(A): def __init__(self): super().__init__() # Calls A.__init__ print("C init")
class D(B, C): def __init__(self): super().__init__() # Calls B.__init__ (MRO) print("D init")
obj = D() # Output: A init, C init, B init, D init (MRO: D -> B -> C -> A)
class B(A): def __init__(self): super().__init__() # Calls A.__init__ print("B init")
class C(A): def __init__(self): super().__init__() # Calls A.__init__ print("C init")
class D(B, C): def __init__(self): super().__init__() # Calls B.__init__ (MRO) print("D init")
obj = D() # Output: A init, C init, B init, D init (MRO: D -> B -> C -> A)
What is the difference between issubclass() and isinstance()?
issubclass(class1, class2):
Checks if class1 is a subclass of class2 (direct or indirect).isinstance(obj, class):
Checks if obj is an instance of class or its subclass.
class A: pass
class B(A): pass
class C(B): pass
obj = C()
print(issubclass(C, A)) # True (C inherits from A via B) print(issubclass(C, B)) # True (direct subclass) print(issubclass(C, C)) # True (class is subclass of itself)
print(isinstance(obj, A)) # True (obj is instance of A's subclass) print(isinstance(obj, B)) # True (obj is instance of B's subclass) print(isinstance(obj, C)) # True (direct instance)
obj = C()
print(issubclass(C, A)) # True (C inherits from A via B) print(issubclass(C, B)) # True (direct subclass) print(issubclass(C, C)) # True (class is subclass of itself)
print(isinstance(obj, A)) # True (obj is instance of A's subclass) print(isinstance(obj, B)) # True (obj is instance of B's subclass) print(isinstance(obj, C)) # True (direct instance)
How to prevent inheritance in Python?
Use a metaclass or raise error in __init_subclass__. Python doesn't have final/ sealed classes like Java.
class FinalClass:
def __init_subclass__(cls):
raise TypeError("Cannot subclass FinalClass")
# This will work: obj = FinalClass()
# This will fail: # class Child(FinalClass): # Raises TypeError # pass
# This will work: obj = FinalClass()
# This will fail: # class Child(FinalClass): # Raises TypeError # pass
# Alternative using metaclass
class FinalMeta(type):
def __new__(cls, name, bases, dct):
if bases: # If has parent classes
raise TypeError("Cannot inherit from final class")
return super().__new__(cls, name, bases, dct)
class FinalClass(metaclass=FinalMeta): pass
class FinalClass(metaclass=FinalMeta): pass
What is method overriding in inheritance?
Method overriding: Child class provides its own implementation of a method already defined in parent class.
class Animal:
def sound(self):
return "Some sound"
class Dog(Animal): def sound(self): # Override parent method return "Bark"
class Cat(Animal): def sound(self): # Override parent method return "Meow"
dog = Dog() cat = Cat() print(dog.sound()) # "Bark" (overridden) print(cat.sound()) # "Meow" (overridden)
Use super() to call parent's overridden method.
class Dog(Animal): def sound(self): # Override parent method return "Bark"
class Cat(Animal): def sound(self): # Override parent method return "Meow"
dog = Dog() cat = Cat() print(dog.sound()) # "Bark" (overridden) print(cat.sound()) # "Meow" (overridden)
How to access parent class methods in multiple inheritance?
Use super() or call parent class directly using class name.
class A:
def method(self):
return "A"
class B: def method(self): return "B"
class C(A, B): def method(self): # Method 1: Using super() (follows MRO) parent_result = super().method() # Calls A.method()
# Method 2: Direct call b_result = B.method(self) # Explicitly call B.method
return f"C: {parent_result}, {b_result}"
obj = C() print(obj.method()) # "C: A, B"
class B: def method(self): return "B"
class C(A, B): def method(self): # Method 1: Using super() (follows MRO) parent_result = super().method() # Calls A.method()
# Method 2: Direct call b_result = B.method(self) # Explicitly call B.method
return f"C: {parent_result}, {b_result}"
obj = C() print(obj.method()) # "C: A, B"
What are abstract base classes and inheritance?
Abstract Base Classes (ABCs) define interfaces that subclasses must implement. Use abc module.
from abc import ABC, abstractmethod
class Shape(ABC): # Abstract base class @abstractmethod def area(self): pass # Must be implemented by subclasses
@abstractmethod def perimeter(self): pass
class Rectangle(Shape): # Concrete class def __init__(self, width, height): self.width = width self.height = height
def area(self): # Must implement return self.width * self.height
def perimeter(self): # Must implement return 2 * (self.width + self.height)
# shape = Shape() # Error! Cannot instantiate ABC rect = Rectangle(5, 3) # OK print(rect.area()) # 15
class Shape(ABC): # Abstract base class @abstractmethod def area(self): pass # Must be implemented by subclasses
@abstractmethod def perimeter(self): pass
class Rectangle(Shape): # Concrete class def __init__(self, width, height): self.width = width self.height = height
def area(self): # Must implement return self.width * self.height
def perimeter(self): # Must implement return 2 * (self.width + self.height)
# shape = Shape() # Error! Cannot instantiate ABC rect = Rectangle(5, 3) # OK print(rect.area()) # 15
What is composition vs inheritance? When to use each?
Inheritance:
"Is-a" relationship. Child class IS a type of parent class.Composition:
"Has-a" relationship. Class HAS another class as component.
# Inheritance: Car IS a Vehicle
class Vehicle: pass
class Car(Vehicle): pass # Car is a Vehicle
# Composition: Car HAS an Engine class Engine: pass class Car: def __init__(self): self.engine = Engine() # Car has an Engine
# Composition: Car HAS an Engine class Engine: pass class Car: def __init__(self): self.engine = Engine() # Car has an Engine
Guideline: Use inheritance for specialization (is-a), composition for code reuse (has-a). Favor composition over inheritance when possible.
How to check inheritance hierarchy in Python?
Use __bases__ and __subclasses__() attributes.
class A: pass
class B(A): pass
class C(B): pass
class D(B): pass
# Check parent classes print(C.__bases__) # (<class '__main__.B'>,) print(B.__bases__) # (<class '__main__.A'>,)
# Check subclasses print(A.__subclasses__()) # [<class '__main__.B'>] print(B.__subclasses__()) # [<class '__main__.C'>, <class '__main__.D'>]
# MRO print(C.__mro__) # (C, B, A, object)
# Check parent classes print(C.__bases__) # (<class '__main__.B'>,) print(B.__bases__) # (<class '__main__.A'>,)
# Check subclasses print(A.__subclasses__()) # [<class '__main__.B'>] print(B.__subclasses__()) # [<class '__main__.C'>, <class '__main__.D'>]
# MRO print(C.__mro__) # (C, B, A, object)
What is the difference between classical and new-style inheritance in Python?
In Python 2, there were "classic classes" and "new-style classes". Python 3 only has new-style classes (all classes inherit from object).
# Python 2 (obsolete):
# Classic class (no object inheritance)
class OldClass: # Bad MRO, limited features
pass
# New-style class class NewClass(object): # Better MRO, more features pass
# Python 3 (always new-style): class MyClass: # Implicitly inherits from object pass
print(MyClass.__bases__) # (<class 'object'>,)
# New-style class class NewClass(object): # Better MRO, more features pass
# Python 3 (always new-style): class MyClass: # Implicitly inherits from object pass
print(MyClass.__bases__) # (<class 'object'>,)
How to implement interface-like behavior in Python?
Python doesn't have interfaces like Java. Use Abstract Base Classes (ABCs) or Protocols (Python 3.8+).
from abc import ABC, abstractmethod
class Drawable(ABC): # Interface-like @abstractmethod def draw(self): pass
@abstractmethod def resize(self, factor): pass
class Circle(Drawable): def draw(self): return "Drawing circle"
def resize(self, factor): return f"Resizing by {factor}"
# Protocol (Python 3.8+) from typing import Protocol
class Drawable(Protocol): def draw(self) -> str: ... def resize(self, factor: float) -> str: ...
class Drawable(ABC): # Interface-like @abstractmethod def draw(self): pass
@abstractmethod def resize(self, factor): pass
class Circle(Drawable): def draw(self): return "Drawing circle"
def resize(self, factor): return f"Resizing by {factor}"
# Protocol (Python 3.8+) from typing import Protocol
class Drawable(Protocol): def draw(self) -> str: ... def resize(self, factor: float) -> str: ...
What is Cooperative Multiple Inheritance?
Cooperative multiple inheritance uses super() to call next method in MRO, allowing all parents to participate.
class A:
def method(self):
print("A")
return "A"
class B: def method(self): print("B") result = super().method() # Calls next in MRO return f"B + {result}"
class C(A): def method(self): print("C") result = super().method() # Calls A.method() return f"C + {result}"
class D(B, C): # MRO: D -> B -> C -> A def method(self): print("D") result = super().method() # Calls B.method() return f"D + {result}"
obj = D() print(obj.method()) # Output: D, B, C, A # Result: "D + B + C + A"
class B: def method(self): print("B") result = super().method() # Calls next in MRO return f"B + {result}"
class C(A): def method(self): print("C") result = super().method() # Calls A.method() return f"C + {result}"
class D(B, C): # MRO: D -> B -> C -> A def method(self): print("D") result = super().method() # Calls B.method() return f"D + {result}"
obj = D() print(obj.method()) # Output: D, B, C, A # Result: "D + B + C + A"
What are mixins in Python inheritance?
Mixins are small classes that provide additional functionality to other classes. They're not meant to be instantiated alone.
class JSONMixin: # Mixin class
def to_json(self):
import json
return json.dumps(self.__dict__)
class XMLMixin: # Another mixin def to_xml(self): # XML conversion logic return "<object>...</object>"
class Person: def __init__(self, name, age): self.name = name self.age = age
class JSONPerson(JSONMixin, Person): # Add JSON capability pass
class XMLPerson(XMLMixin, Person): # Add XML capability pass
class FullPerson(JSONMixin, XMLMixin, Person): # Multiple mixins pass
p = FullPerson("Alice", 30) print(p.to_json()) # From JSONMixin print(p.to_xml()) # From XMLMixin
class XMLMixin: # Another mixin def to_xml(self): # XML conversion logic return "<object>...</object>"
class Person: def __init__(self, name, age): self.name = name self.age = age
class JSONPerson(JSONMixin, Person): # Add JSON capability pass
class XMLPerson(XMLMixin, Person): # Add XML capability pass
class FullPerson(JSONMixin, XMLMixin, Person): # Multiple mixins pass
p = FullPerson("Alice", 30) print(p.to_json()) # From JSONMixin print(p.to_xml()) # From XMLMixin
Best Practice: Name mixins with "Mixin" suffix and place them before main classes in inheritance list.
Note: Python supports all major inheritance types with unique features like MRO to handle the Diamond Problem. Remember: Single (1 parent), Multiple (>1 parent), Multilevel (chain), Hierarchical (1 parent, many children), Hybrid (combination). Always use super() for cooperative inheritance and be aware of method resolution order.