Menu

Python Inheritance: Subclasses, super(), Overriding, and the MRO

How class inheritance works in Python: creating a subclass, calling super().__init__, overriding and extending methods, isinstance and issubclass, multiple inheritance and the MRO, and when composition is the better choice.

This page includes runnable editors - edit, run, and see output instantly.

A Class Built on Another Class

Inheritance lets you define a new class as an extension of an existing one. The existing class is the parent (also called the base class or superclass), and the new one is the child (subclass). The child gets every method and class attribute of the parent without repeating them, and it can add its own or replace the ones that should behave differently.

You declare the parent in parentheses after the class name:

Output:

Rex is an animal
Woof

Dog has no __init__ and no describe of its own. When you call Dog("Rex"), Python looks for __init__ on Dog, does not find it, and uses the one on Animal. The same lookup happens for rex.describe(). For rex.speak() it finds speak on Dog first and stops there.

That lookup order is the whole mechanism: Python searches the object's own class, then the parent, then the parent's parent, and uses the first match. If you are new to classes, the classes page covers __init__, self and methods first.

Overriding a Method

A method in the child with the same name as one in the parent replaces it for instances of the child. This is called overriding. Each subclass can answer the same call in its own way:

Output:

Rectangle with area 12
Square with area 25
Shape with area 0

summary is defined once, on Shape, but it calls self.area(). Because self is the actual object, a Rectangle runs Rectangle.area and a Square finds area on its parent Rectangle. The parent's code calls the child's method without knowing the child exists. This is what makes inheritance useful for sharing a common workflow while letting each subclass fill in the details.

Calling the Parent With super()

When a child defines its own __init__, the parent's __init__ no longer runs on its own. If the child still needs the parent's setup, call it with super().__init__():

Output:

Ada 1050.0

super() returns an object that looks up methods starting from the parent of the current class. super().__init__(owner, balance) therefore runs Account.__init__ on the same object, and self.owner and self.balance exist afterwards.

Leave that line out and the object is only half built:

Output:

0.05
AttributeError: 'SavingsAccount' object has no attribute 'owner'

You can write Account.__init__(self, owner) instead, and older code often does. super() is preferred because it does not repeat the parent's name and it cooperates correctly with multiple inheritance, covered below.

Extending a Method Instead of Replacing It

super() works in any method, not only __init__. Calling the parent's version inside an override lets you add behavior around it instead of rewriting it:

Output:

started
[12:00] started
INFO [12:00] started

Each level adds one piece and delegates the rest upwards. If the parent's format changes later, both children pick up the change.

isinstance and issubclass

A subclass instance is also an instance of every class above it. isinstance(obj, cls) checks that relationship, and issubclass(child, parent) checks it between two classes:

Prefer isinstance over comparing type(obj) directly. A check like type(obj) is Animal rejects every subclass, which breaks as soon as someone adds Dog.

Every class you write inherits from object, even when you do not say so. class Animal: is the same as class Animal(object):. That is where default methods such as __str__, __repr__ and __eq__ come from, and why overriding __str__ changes what print() shows:

Output:

(2, 3)
True

Requiring Subclasses to Implement a Method

Sometimes the parent should never be used directly, only as a template. The abc module marks such a class as abstract. Any method decorated with @abstractmethod must be overridden before the class can be instantiated:

The first three lines of output are the export:

exporting 2 rows
1,2
3,4

The last line is a TypeError that names Exporter and its missing export method (the exact wording differs between Python versions). The mistake surfaces when the object is created, not later when export is finally called.

Multiple Inheritance and the MRO

A class can list more than one parent. It then inherits from all of them:

Output:

paddling flapping
swims
['Duck', 'Swimmer', 'Flyer', 'object']

Both parents define move, and Duck gets the one from Swimmer. Python decides with the method resolution order (MRO): a single list of classes, stored in Duck.__mro__, that every attribute lookup walks from front to back. It starts with the class itself, keeps the parents in the order you listed them, puts every class before its own parents, and ends with object. Swap the order to class Duck(Flyer, Swimmer) and move returns "flies".

super() follows the same list. Inside a method of Swimmer, super() means "the next class after Swimmer in the MRO of the object's class", which for a Duck is Flyer, not object. That is how cooperative classes can each call super() and have every class in the chain run exactly once, even in a diamond where two parents share a grandparent.

In practice, multiple inheritance is mostly used for mixins: small classes that add one capability, such as JsonMixin adding a to_json method, combined with one main parent. Deep hierarchies with several full parents are hard to follow and rarely needed.

Inheritance or Composition?

Inheritance models an "is a" relationship. A SavingsAccount is an Account: any code that works with an Account should work with it unchanged. When that sentence sounds wrong, you probably want composition, which models "has a": the object keeps another object as an attribute and calls it.

Output:

Roadster: 300 hp engine running

class Car(Engine) would also run, but it would claim that a car is a kind of engine, give Car every engine method, and make it impossible to swap in a different engine without a different class. With composition the parts stay separate, and replacing the engine is a matter of passing another object.

A few signs that inheritance is the wrong tool:

  • The child overrides most of the parent's methods, or disables some of them.
  • You inherit only to reuse one or two helper methods.
  • The hierarchy grows past two or three levels and you have to read several files to see what a method does.

Common Mistakes

  • Forgetting super().__init__(). The child's __init__ replaces the parent's, so the parent's attributes are never set and you get an AttributeError later.
  • Passing self to super() calls. Write super().__init__(name), not super().__init__(self, name). super() already binds the method to the current object.
  • Mismatched signatures when overriding. If the parent's speak(self) is called by shared code, a child's speak(self, volume) with a required extra argument breaks that code. Keep overrides compatible, or give new parameters defaults.
  • Checking type(obj) == SomeClass. It ignores subclasses. Use isinstance.
  • Using inheritance to share code between unrelated classes. Move the shared code into a function or a helper object instead.

Frequently Asked Questions

How do you inherit from a class in Python?

Put the parent class in parentheses after the new class name: class Dog(Animal):. Dog gets every method and class attribute of Animal, and can add new ones or override existing ones.

What does super().__init__() do?

It calls the parent class's __init__ so the parent can set up its own attributes. When a subclass defines its own __init__, Python does not call the parent's automatically, so you call super().__init__(...) with the arguments the parent needs, then set the subclass's extra attributes.

What is the difference between isinstance and type in Python?

isinstance(obj, Animal) is True for an Animal and for instances of any subclass of Animal. type(obj) is Animal is True only when the object was created from Animal itself. Prefer isinstance, because it keeps working when someone adds a subclass.

Does Python support multiple inheritance?

Yes: class C(A, B): inherits from both. When both parents define the same method, Python picks one by following the method resolution order (MRO), which you can inspect with C.__mro__. It lists C first, then its parents from left to right, and object last.

When should I use composition instead of inheritance?

Use inheritance for an "is a" relationship, where the subclass can be used anywhere the parent is expected (a SavingsAccount is an Account). Use composition, storing an object as an attribute, for a "has a" relationship (a Car has an Engine). When unsure, composition is easier to change later.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED