Python
Programming
Inheritance
Python Classes
Object-Oriented Programming

How to get the parents of a Python class?

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

Introduction

Python provides several built-in ways to inspect a class's parent classes (superclasses). The __bases__ attribute gives direct parents, __mro__ (Method Resolution Order) gives the full inheritance chain, and inspect.getmro() provides the same chain via the standard library. Understanding these tools is essential for debugging complex inheritance hierarchies, especially with multiple inheritance and mixins.

bases: Direct Parents Only

python
1class Animal:
2    pass
3
4class Mammal(Animal):
5    pass
6
7class Dog(Mammal):
8    pass
9
10print(Dog.__bases__)     # (<class '__main__.Mammal'>,)
11print(Mammal.__bases__)  # (<class '__main__.Animal'>,)
12print(Animal.__bases__)  # (<class 'object'>,)

__bases__ is a tuple of the direct parent classes, going only one level up. Every class that does not explicitly inherit from anything implicitly inherits from object.

Multiple Inheritance

python
1class Flyable:
2    pass
3
4class Swimmable:
5    pass
6
7class Duck(Flyable, Swimmable):
8    pass
9
10print(Duck.__bases__)  # (<class '__main__.Flyable'>, <class '__main__.Swimmable'>)

With multiple inheritance, __bases__ contains all direct parents in the order they were specified.

mro: Full Inheritance Chain

python
1class A:
2    pass
3
4class B(A):
5    pass
6
7class C(A):
8    pass
9
10class D(B, C):
11    pass
12
13print(D.__mro__)
14# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
15
16# Also available as a method:
17print(D.mro())
18# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

__mro__ (Method Resolution Order) shows the complete chain Python follows when looking up methods. It uses the C3 linearization algorithm to produce a consistent ordering, even with diamond inheritance patterns.

inspect.getmro(): Standard Library Approach

python
1import inspect
2
3class Base:
4    pass
5
6class Middle(Base):
7    pass
8
9class Child(Middle):
10    pass
11
12parents = inspect.getmro(Child)
13print(parents)
14# (<class 'Child'>, <class 'Middle'>, <class 'Base'>, <class 'object'>)
15
16# Get just the parent names
17parent_names = [cls.__name__ for cls in inspect.getmro(Child)]
18print(parent_names)  # ['Child', 'Middle', 'Base', 'object']

inspect.getmro() returns the same result as __mro__ but works with old-style classes in Python 2 (less relevant now, but the convention persists in many codebases).

issubclass() and isinstance()

python
1class Vehicle:
2    pass
3
4class Car(Vehicle):
5    pass
6
7class ElectricCar(Car):
8    pass
9
10# issubclass checks the entire inheritance chain
11print(issubclass(ElectricCar, Vehicle))  # True
12print(issubclass(ElectricCar, Car))      # True
13print(issubclass(Car, ElectricCar))      # False, direction matters
14
15# isinstance checks if an object is an instance of a class or its parents
16my_car = ElectricCar()
17print(isinstance(my_car, Vehicle))       # True
18print(isinstance(my_car, Car))           # True
19print(isinstance(my_car, ElectricCar))   # True

Practical: Finding All Parent Methods

python
1import inspect
2
3class Base:
4    def base_method(self):
5        pass
6
7class Mixin:
8    def mixin_method(self):
9        pass
10
11class Child(Base, Mixin):
12    def child_method(self):
13        pass
14
15# Get all methods, including inherited ones
16all_methods = inspect.getmembers(Child, predicate=inspect.isfunction)
17print([name for name, _ in all_methods])
18# ['base_method', 'child_method', 'mixin_method']
19
20# Find which class defines each method
21for name, method in all_methods:
22    for cls in inspect.getmro(Child):
23        if name in cls.__dict__:
24            print(f"{name} defined in {cls.__name__}")
25            break
26# base_method defined in Base
27# child_method defined in Child
28# mixin_method defined in Mixin

Walking the Hierarchy

python
1def print_class_tree(cls, indent=0):
2    """Print the inheritance tree of a class."""
3    print("  " * indent + cls.__name__)
4    for base in cls.__bases__:
5        if base is not object:
6            print_class_tree(base, indent + 1)
7
8class A: pass
9class B(A): pass
10class C(A): pass
11class D(B, C): pass
12
13print_class_tree(D)
14# D
15#   B
16#     A
17#   C
18#     A

super() and MRO

python
1class A:
2    def greet(self):
3        print("A")
4
5class B(A):
6    def greet(self):
7        print("B")
8        super().greet()
9
10class C(A):
11    def greet(self):
12        print("C")
13        super().greet()
14
15class D(B, C):
16    def greet(self):
17        print("D")
18        super().greet()
19
20D().greet()
21# D
22# B
23# C
24# A
25# super() follows __mro__: D → B → C → A

super() does not go to the "parent". It goes to the next class in the MRO. This is why B.greet() calls C.greet() (not A.greet()) when called through D.

Common Pitfalls

  • Confusing __bases__ with __mro__: __bases__ gives only direct parents (one level). __mro__ gives the entire chain including grandparents and object. Use __mro__ when you need the full hierarchy.
  • Assuming super() calls the direct parent: In multiple inheritance, super() follows the MRO, which may skip to a sibling class before reaching a common ancestor. This is by design (cooperative multiple inheritance).
  • Checking __bases__ on an instance: __bases__ is a class attribute, not an instance attribute. Use type(instance).__bases__ or instance.__class__.__bases__.
  • Diamond inheritance without super(): If parent classes call ParentClass.method(self) instead of super().method(), a method in the common ancestor gets called multiple times. Always use super() for cooperative inheritance.
  • Forgetting object in the chain: Every class in Python 3 implicitly inherits from object. The MRO always ends with object, which is the root of all classes.

Summary

  • cls.__bases__ returns a tuple of direct parent classes
  • cls.__mro__ or cls.mro() returns the full Method Resolution Order chain
  • inspect.getmro(cls) provides the same MRO via the standard library
  • issubclass(A, B) checks if A is anywhere in B's inheritance tree
  • super() follows the MRO, not the direct parent. This is important for multiple inheritance
  • The C3 linearization algorithm ensures a consistent, predictable method lookup order

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

All Rights Reserved.