Inheritance lets one Python class reuse and specialize behavior defined by another class. It can be useful when classes have a genuine “is-a” relationship and share a stable interface.

Basic Inheritance

class Animal:
    def sound(self):
        return "unknown"


class Dog(Animal):
    def sound(self):
        return "bark"


print(Dog().sound())

Dog inherits from Animal and overrides sound() with behavior specific to dogs.

Inherit an Initializer

If a subclass does not define its own __init__(), Python uses the inherited one:

class Animal:
    def __init__(self, name):
        self.name = name


class Dog(Animal):
    pass


dog = Dog("Milo")
print(dog.name)

Extend Parent Initialization with super()

When a subclass needs extra state, call the parent implementation through super():

class Animal:
    def __init__(self, name):
        self.name = name


class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed

super() follows Python’s method resolution order (MRO), which matters especially in multiple inheritance.

Override and Reuse Parent Behavior

class Logger:
    def message(self):
        return "base message"


class DetailedLogger(Logger):
    def message(self):
        base = super().message()
        return f"{base} with details"

Multiple Inheritance

Python supports multiple base classes:

class CanFly:
    def fly(self):
        return "flying"


class CanSwim:
    def swim(self):
        return "swimming"


class Duck(CanFly, CanSwim):
    pass

Multiple inheritance can be useful for small, well-defined mixins, but large inheritance graphs are difficult to understand. Inspect the MRO when behavior depends on several bases:

print(Duck.mro())

Prefer Composition When the Relationship Is Not “Is-A”

Inheritance is not the only way to reuse code. If one object merely uses another service, composition is often clearer:

class Engine:
    def start(self):
        return "started"


class Car:
    def __init__(self, engine):
        self.engine = engine

    def start(self):
        return self.engine.start()

A car has an engine; it is not an engine. Composition models that relationship directly.

isinstance() and issubclass()

print(isinstance(Dog("Milo", "Beagle"), Animal))
print(issubclass(Dog, Animal))

These checks can be useful, but Python often favors behavior-based interfaces over frequent explicit type checks.

Conclusion

Inheritance is valuable for genuine class hierarchies and shared contracts. Use super() correctly, keep hierarchies shallow when possible, and prefer composition when objects collaborate rather than represent specialized forms of one another.