🎓 Velnov

🐍 Python · Topic 15 of 20

Inheritance & Polymorphism — Python Questions with Answers

Start with the study notes on Inheritance & Polymorphism (definitions, examples and a quick revision), then work through 30 practice questions, each with the correct answer and a clear explanation of why it is right. When you are ready, take this topic as an interactive quiz to test yourself.

7 easy · 16 medium · 7 hard · Part of Python: Master Python from basics to advanced - 20 topics.

Topic overview

Inheritance and Polymorphism – building on other classes

1. Inheritance

Inheritance lets a child class automatically get the attributes and methods of a parent class. Write the shared code once, in the parent.

class Animal:
    def eat(self):
        print("eating")

class Dog(Animal):        # Dog inherits from Animal
    def fetch(self):
        print("fetching")

d = Dog()
d.eat()                   # inherited
d.fetch()                 # Dog's own method
Outputeating
fetching

Example: Inheritance means “is-a”: a Dog is an Animal, a Car is a Vehicle.

2. Overriding methods

A child can override a parent's method by defining one with the same name. Python looks in the object's own class first, then climbs up to the parent.

class Animal:
    def speak(self):
        return "some sound"

class Cat(Animal):
    def speak(self):
        return "Meow"

print(Cat().speak())
print(Animal().speak())   # the parent is unchanged
OutputMeow
some sound

3. super()

super() calls the parent's version of a method. Its most common use is running the parent's __init__, because overriding __init__ means the parent's one no longer runs by itself.

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)    # let the parent set name
        self.breed = breed

d = Dog("Rex", "Labrador")
print(d.name, d.breed)
OutputRex Labrador

Remember: If you forget super().__init__(name), self.name is never set, and reading it gives AttributeError.

Extending instead of replacing

class A:
    def greet(self):
        return "hi from A"

class B(A):
    def greet(self):
        return super().greet() + " and B"

print(B().greet())
Outputhi from A and B

4. isinstance() and issubclass()

class Animal: pass
class Dog(Animal): pass

d = Dog()
print(isinstance(d, Dog), isinstance(d, Animal))
print(type(d) == Animal)          # exact type only
print(issubclass(Dog, Animal), issubclass(bool, int))
OutputTrue True
False
True True

Remember: Prefer isinstance(): it accepts subclasses, while type(x) == Dog rejects them.

5. Polymorphism

Polymorphism means different classes answering the same method call in their own way. The calling code doesn't need to know which class it has.

class Dog:
    def speak(self): return "Woof"
class Cat:
    def speak(self): return "Meow"
class Robot:
    def speak(self): return "Beep"

for thing in [Dog(), Cat(), Robot()]:
    print(thing.speak())
OutputWoof
Meow
Beep

Duck typing

“If it walks like a duck and quacks like a duck…”: Python cares what an object can do, not what class it comes from. The Robot above works without any shared parent class.

6. Composition: “has-a”

Composition means storing another object as an attribute. Use it when the relationship is “has-a” rather than “is-a”.

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

class Car:
    def __init__(self):
        self.engine = Engine()    # a Car HAS an Engine
    def start(self):
        return self.engine.start()

print(Car().start())
Outputvroom

Remember: Common advice: prefer composition over inheritance unless “is-a” clearly fits.

7. Multiple inheritance and the MRO

A class can have several parents. When they share a method name, Python follows the Method Resolution Order (MRO), checking parents left to right.

class A:
    def hello(self): return "A"
class B:
    def hello(self): return "B"
class C(A, B):
    pass

print(C().hello())
print([k.__name__ for k in C.__mro__])
OutputA
['C', 'A', 'B', 'object']

Example: Every class ultimately inherits from object.

8. Methods children must write

A parent can force children to provide a method: raise NotImplementedError, or use the abc module's @abstractmethod.

class Shape:
    def area(self):
        raise NotImplementedError("subclass must define area")

class Square(Shape):
    def __init__(self, side):
        self.side = side
    def area(self):
        return self.side ** 2

print(Square(3).area())
Output9

9. Common misconceptions

MisconceptionCorrect understanding
The parent's __init__ runs automatically even if the child defines its own.Overriding __init__ replaces it; call super().__init__().
Overriding a method changes the parent class.Only the child's lookup changes; the parent keeps its version.
Polymorphism needs a shared parent class.Python uses duck typing; any object with the method works.
A Car should inherit from Engine.A Car has an Engine; use composition.
type(x) == Parent is the best check.isinstance(x, Parent) also accepts subclasses.

10. Quick revision

Inheritance
A child class gets a parent's methods.
Override
Redefine a parent's method in the child.
super()
Calls the parent's version of a method.
Polymorphism
Same method call, different behaviour.
Duck typing
What an object can do matters, not its class.
Composition
“Has-a”: an object stored as an attribute.
MRO
The order Python searches classes for a method.
object
The root of every class.

11. Test your understanding

  1. How do you make Dog inherit from Animal?

    Show answer

    class Dog(Animal):

  2. Both classes define speak(). Which runs for Dog().speak()?

    Show answer

    Dog's version (it overrides the parent's).

  3. What is super() most often used for?

    Show answer

    Calling the parent's __init__.

  4. d = Dog(), Dog inherits Animal. What is isinstance(d, Animal)?

    Show answer

    True

  5. What is polymorphism?

    Show answer

    Different classes responding to the same method call in their own way.

  6. What is duck typing?

    Show answer

    Caring about what an object can do, not what class it is.

  7. Should Car inherit from Engine?

    Show answer

    No: a Car has an Engine, so use composition.

  8. class C(A, B): both define hello(). Which runs?

    Show answer

    A's, because the MRO checks parents left to right.

Key takeaway: Inherit for “is-a” relationships, override to specialise, call super() to keep the parent's work, and let polymorphism treat different objects the same way.

Notes written by Velnov. Every code example with an output was run in Python 3 to check that the output shown is exactly what Python prints.

Key concepts in this topic

Code reuseis-a relationshipSyntaxParent classesMethod lookupInheritanceOverridingMROsuper()__init__ chainingAttributeErrorisinstance()is-aissubclass()Class relationshipsPolymorphismDuck typingProtocolsIsolationExtensionInheritance directionis-a vs has-aCompositionDesign principles

Jump to: Easy · Medium · Hard

Easy questions (7)

Q1. easy

What is inheritance in OOP?

class Animal:
    def eat(self):
        print("eating")

class Dog(Animal):     # Dog inherits eat()
    pass

Dog().eat()             # eating
  1. A A class (child) automatically gets the attributes and methods of another class (parent)
  2. B Copying code between files
  3. C Objects sharing memory
  4. D Importing modules

Answer: A — A class (child) automatically gets the attributes and methods of another class (parent)

Inheritance lets you build a specialized class on top of a general one: Dog inherits everything Animal has, then adds or changes what it needs. Write the common code once.

Code reuseis-a relationship

Q2. easy

Which syntax makes Dog inherit from Animal?

  1. A class Dog(Animal):
  2. B class Dog extends Animal:
  3. C class Dog inherits Animal:
  4. D class Animal(Dog):

Answer: A — class Dog(Animal):

The parent goes in parentheses after the child's name. (extends is Java; option D is backwards - it would make Animal the child.)

SyntaxParent classes

Q3. easy

class Cat(Animal): pass - and Animal has a sleep() method. What does Cat().sleep() do?

class Animal:
    def sleep(self):
        print("zzz")

class Cat(Animal):
    pass

Cat().sleep()   # zzz
  1. A Works - Cat inherited sleep() from Animal
  2. B AttributeError - Cat is empty
  3. C Returns None only
  4. D SyntaxError

Answer: A — Works - Cat inherited sleep() from Animal

An empty child still owns everything the parent defined. Python looks for sleep on Cat, doesn't find it, and walks UP to Animal - found, called.

Method lookupInheritance

Q7. easy

dog = Dog() where Dog inherits Animal. What is isinstance(dog, Animal)?

print(isinstance(dog, Dog))      # True
print(isinstance(dog, Animal))   # True
print(type(dog) == Animal)        # False - exact type only
  1. A True - an instance of a subclass IS an instance of the parent too
  2. B False - dog is only a Dog
  3. C Error
  4. D None

Answer: A — True - an instance of a subclass IS an instance of the parent too

isinstance() respects inheritance: every Dog IS an Animal (the 'is-a' relationship). That's why isinstance is preferred over type() == for checks.

isinstance()is-a

Q12. easy

Can a child class ADD methods the parent doesn't have?

class Dog(Animal):
    def fetch(self):
        print("fetching!")

Dog().fetch()      # fine
# Animal().fetch() # AttributeError
  1. A Yes - children extend parents freely (e.g. Dog adds fetch())
  2. B No - children can only override
  3. C Only one new method
  4. D Only attributes, not methods

Answer: A — Yes - children extend parents freely (e.g. Dog adds fetch())

Children = parent's features + anything new. Dog gets eat() and sleep() from Animal and adds fetch(). Note Animal instances do NOT get fetch - inheritance flows downward only.

ExtensionInheritance direction

Q23. easy

In class Rectangle(Shape): - which is the parent (base) class?

  1. A Shape
  2. B Rectangle
  3. C Both
  4. D Neither

Answer: A — Shape

The class in parentheses is the parent (also called base or super class); Rectangle is the child (derived/sub class).

Terminology

Q27. easy

What is the main PURPOSE of inheritance?

  1. A Reuse and organize shared behavior - write common code once in the parent
  2. B Make programs run faster
  3. C Hide code from other developers
  4. D It's required for classes to work

Answer: A — Reuse and organize shared behavior - write common code once in the parent

Shared logic lives once in the base class; specializations only express their differences. Less duplication, clearer structure - though don't force it where 'is-a' doesn't hold.

Code reuseDRY

Medium questions (16)

Q4. medium

Class Dog inherits from Animal. Both define speak(). What runs for Dog().speak()?

class Animal:
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "Woof!"

print(Dog().speak())   # Woof!
  1. A Animal.speak() - parents win
  2. B Dog.speak() - the child's version OVERRIDES the parent's
  3. C Both run automatically
  4. D TypeError - ambiguous

Answer: B — Dog.speak() - the child's version OVERRIDES the parent's

Method overriding: Python checks the instance's own class FIRST. Dog.speak() shadows Animal.speak(). The parent's version still exists - reachable via super().speak().

OverridingMRO

Q5. medium

What does super() give you inside a child class?

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # parent sets name
        self.breed = breed        # child adds breed
  1. A Access to the PARENT class's methods - e.g. super().__init__(...)
  2. B A more powerful version of self
  3. C The grandchild class
  4. D Administrator rights

Answer: A — Access to the PARENT class's methods - e.g. super().__init__(...)

super() reaches the next class up the chain - most commonly used to run the parent's __init__ so its setup isn't lost when the child overrides it.

super()__init__ chaining

Q8. medium

What does issubclass(Dog, Animal) check?

print(issubclass(Dog, Animal))    # True
print(issubclass(bool, int))       # True - fun fact!
  1. A Whether the Dog CLASS inherits from Animal - True here
  2. B Whether dog objects exist
  3. C Whether Animal inherits from Dog
  4. D Nothing - not a real function

Answer: A — Whether the Dog CLASS inherits from Animal - True here

issubclass compares CLASSES (isinstance compares an object to a class). issubclass(Dog, Animal) is True; issubclass(Animal, Dog) is False.

issubclass()Class relationships

Q9. medium

What is polymorphism?

for animal in [Dog(), Cat(), Cow()]:
    print(animal.speak())   # Woof! Meow! Moo!
# one loop, three behaviors
  1. A Different classes responding to the SAME method call in their own way
  2. B One class with many names
  3. C Multiple inheritance
  4. D Converting types

Answer: A — Different classes responding to the SAME method call in their own way

Poly = many, morph = forms. Call speak() on any animal - each class provides its own version, and the calling code doesn't care which: the same line of code works for every type.

PolymorphismOverriding

Q11. medium

When the child overrides a method, is the PARENT class changed?

class Cat(Animal):
    pass

print(Dog().speak())   # Woof! (overridden)
print(Cat().speak())   # ...   (parent's original)
  1. A No - Animal.speak() still works exactly as before; only Dog's lookup changed
  2. B Yes, the parent method is replaced everywhere
  3. C The parent method is deleted
  4. D Both methods merge

Answer: A — No - Animal.speak() still works exactly as before; only Dog's lookup changed

Overriding adds a method to the CHILD class; the parent is untouched. Animal instances (and other subclasses) keep the original behavior.

OverridingIsolation

Q13. medium

Which real-world pairs fit INHERITANCE ('is-a') best?

class Car(Vehicle):          # is-a
    def __init__(self):
        self.engine = Engine()  # has-a (composition)
  1. A Car is-a Vehicle; Circle is-a Shape; Admin is-a User
  2. B Car is-a Engine; House is-a Door
  3. C Engine is-a Car
  4. D All pairs fit inheritance

Answer: A — Car is-a Vehicle; Circle is-a Shape; Admin is-a User

Inheritance models 'is-a': every Car IS a Vehicle. A Car HAS an Engine - that's composition (storing an Engine object as an attribute), not inheritance. Choosing wrong leads to awkward classes.

is-a vs has-aComposition

Q15. medium

What is the ultimate ancestor of EVERY Python class?

print(issubclass(Dog, object))   # True
print(issubclass(int, object))   # True
  1. A object
  2. B type
  3. C class
  4. D root

Answer: A — object

All classes inherit from object (explicitly or not) - it provides the defaults: __init__, __str__, __eq__ and more. 'class Dog:' and 'class Dog(object):' are identical in Python 3.

objectClass hierarchy

Q16. medium

Is multiple inheritance allowed? class FlyingCar(Car, Airplane):

class FlyingCar(Car, Airplane):
    pass
# lookup order: FlyingCar -> Car -> Airplane -> object
  1. A Yes - a class may inherit from several parents (listed in order)
  2. B No - Python allows only one parent
  3. C Only with a plugin
  4. D Only for built-in types

Answer: A — Yes - a class may inherit from several parents (listed in order)

Python supports multiple inheritance: FlyingCar gets methods from BOTH parents. When both define the same method, the Method Resolution Order decides (left-to-right: Car wins here).

Multiple inheritanceMRO

Q18. medium

In what ORDER does Python search for a method you call on an object?

  1. A The object's class first, then its parent, then grandparent... up to object
  2. B object first, then down
  3. C Random order
  4. D Alphabetical by class name

Answer: A — The object's class first, then its parent, then grandparent... up to object

Lookup climbs the inheritance chain from most-specific to most-general, stopping at the FIRST match - which is exactly why child methods override parent ones.

Method lookupMRO

Q19. medium

Trace the output: class A: def greet(self): return "hi from A" class B(A): def greet(self): return super().greet() + " and B" print(B().greet())

  1. A hi from A and B
  2. B hi from A
  3. C and B
  4. D Error

Answer: A — hi from A and B

B's greet calls the parent's version via super() (getting 'hi from A') and extends it. This extend-not-replace pattern is what super() is for.

super()Extending methods

Q21. medium

Can you inherit from BUILT-IN types like list?

class Stack(list):
    def push(self, x):
        self.append(x)
    def peek(self):
        return self[-1]

s = Stack()
s.push(5)
print(s.peek())   # 5
  1. A Yes - class Stack(list): adds/changes behavior on top of a real list
  2. B No - built-ins are sealed
  3. C Only from str
  4. D Only in Python 2

Answer: A — Yes - class Stack(list): adds/changes behavior on top of a real list

Built-ins are classes like any other. A Stack(list) gets append, len, iteration for free and can add push/peek. (For dict, collections.UserDict is often smoother.)

Subclassing built-ins

Q22. medium

Which line lets Dog's __init__ accept name AND breed while reusing Animal's name setup?

  1. A def __init__(self, name, breed):\n super().__init__(name)\n self.breed = breed
  2. B def __init__(self, breed):\n self.breed = breed
  3. C def __init__(self, name, breed):\n Animal(name)\n self.breed = breed
  4. D No way to do this

Answer: A — def __init__(self, name, breed):\n super().__init__(name)\n self.breed = breed

Accept all needed data, delegate the parent's part to super().__init__(name), then set what's new. Option C creates a THROWAWAY Animal object instead of initializing self.

super()__init__ patterns

Q24. medium

What is the output? class Bird: def move(self): return "fly" class Penguin(Bird): def move(self): return "waddle" animals = [Bird(), Penguin(), Bird()] print([a.move() for a in animals])

  1. A ['fly', 'waddle', 'fly']
  2. B ['fly', 'fly', 'fly']
  3. C ['waddle', 'waddle', 'waddle']
  4. D Error

Answer: A — ['fly', 'waddle', 'fly']

Each object uses ITS OWN class's move() - Bird flies, Penguin waddles - even inside one uniform loop. Polymorphism in action.

PolymorphismOverriding

Q26. medium

A child needs the parent's method PLUS extra work. The cleanest pattern is:

class LoggedAccount(Account):
    def deposit(self, amt):
        super().deposit(amt)      # original logic
        print(f"logged: +{amt}")  # the extra bit
  1. A Override it, call super().method() inside, then add the extra steps
  2. B Copy-paste the parent's code
  3. C Rename the parent method
  4. D Make everything global

Answer: A — Override it, call super().method() inside, then add the extra steps

Extend, don't duplicate: super() runs the original, your code adds the difference. Parent bug-fixes automatically reach the child.

super()DRY principle

Q29. medium

Grandparent -> Parent -> Child. Child calls a method only Grandparent defines. Does it work?

  1. A Yes - lookup climbs the WHOLE chain, however many levels
  2. B No - only direct parents are searched
  3. C Only with two super() calls
  4. D Only if Parent re-exports it

Answer: A — Yes - lookup climbs the WHOLE chain, however many levels

Inheritance chains any depth: Child -> Parent -> Grandparent -> object are all searched in order. Deep hierarchies work - though beyond 2-3 levels they get hard to follow; prefer shallow designs.

Inheritance chainsDesign advice

Q30. medium

What does method overriding let a subclass do?

class Animal:
    def speak(self):
        return "some sound"

class Cat(Animal):
    def speak(self):        # overrides
        return "Meow"

print(Cat().speak())        # Meow
print(Animal().speak())     # some sound
  1. A Provide its OWN version of a method the parent already defined, replacing it for instances of the subclass
  2. B Delete the parent's method everywhere
  3. C Rename the parent method
  4. D Call two methods at once

Answer: A — Provide its OWN version of a method the parent already defined, replacing it for instances of the subclass

Overriding = redefining an inherited method in the child with the same name. The child's version is used for child instances; the parent (and other subclasses) keep the original. Combine with super() to extend rather than fully replace.

Overridingsuper()Polymorphism

Hard questions (7)

Q6. hard

What breaks here? class Animal: def __init__(self, name): self.name = name class Dog(Animal): def __init__(self, breed): self.breed = breed d = Dog("lab") print(d.name)

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # don't skip this!
        self.breed = breed
  1. A AttributeError - Dog's __init__ REPLACED Animal's, so self.name was never set
  2. B Prints lab
  3. C Prints None
  4. D Nothing breaks

Answer: A — AttributeError - Dog's __init__ REPLACED Animal's, so self.name was never set

Overriding __init__ means the parent's initializer does NOT run automatically - name never gets set. Fix: call super().__init__(name) inside Dog's __init__ (and accept name as a parameter).

  • Forgetting super().__init__() is the #1 inheritance bug.

super()AttributeError

Q10. hard

What is DUCK TYPING?

class Robot:            # no Animal parent!
    def speak(self):
        return "beep"

for thing in [Dog(), Robot()]:
    print(thing.speak())   # works - both can speak
  1. A 'If it walks like a duck and quacks like a duck...' - Python cares about what an object CAN DO, not what class it is
  2. B A special duck class in the standard library
  3. C Typing very fast
  4. D Inheritance from birds

Answer: A — 'If it walks like a duck and quacks like a duck...' - Python cares about what an object CAN DO, not what class it is

Python doesn't require a shared parent class for polymorphism: ANY object with a speak() method works in code that calls .speak(). len() works on anything with __len__. Behavior over ancestry.

Duck typingProtocols

Q14. hard

What is COMPOSITION, and when is it better than inheritance?

class Engine:
    def start(self): print("vroom")

class Car:
    def __init__(self):
        self.engine = Engine()   # has-a
    def start(self):
        self.engine.start()
  1. A Storing other objects as attributes ('has-a') - better when the relationship isn't truly 'is-a'
  2. B Combining two classes into one file
  3. C A stricter form of inheritance
  4. D Composing music with Python

Answer: A — Storing other objects as attributes ('has-a') - better when the relationship isn't truly 'is-a'

A Car HAS an Engine, so Engine is an attribute - not a parent. Composition is flexible (swap engines at runtime, no fragile hierarchies). Modern advice: 'prefer composition over inheritance' unless the is-a test clearly passes.

CompositionDesign principles

Q17. hard

class C(A, B) - both A and B define hello(). Which runs for C().hello()?

class A:
    def hello(self): return "A"
class B:
    def hello(self): return "B"
class C(A, B): pass

print(C().hello())   # A
print(C.__mro__)      # (C, A, B, object)
  1. A A's - the MRO checks parents LEFT to RIGHT as listed
  2. B B's - rightmost wins
  3. C Both run
  4. D TypeError - conflict

Answer: A — A's - the MRO checks parents LEFT to RIGHT as listed

The Method Resolution Order for C(A, B) is C -> A -> B -> object. First match wins: A.hello(). Inspect any class's order with C.__mro__ or C.mro().

MROMultiple inheritance

Q20. hard

How do you make a 'template' method that children MUST implement?

class Shape:
    def area(self):
        raise NotImplementedError("subclass must define area")

class Circle(Shape):
    def area(self):
        return 3.14159 * self.r ** 2
  1. A Raise NotImplementedError in the parent (or use the abc module's @abstractmethod)
  2. B Leave the method out entirely
  3. C Name it _must_implement
  4. D Python can't express this

Answer: A — Raise NotImplementedError in the parent (or use the abc module's @abstractmethod)

The simple way: the parent's method raises NotImplementedError, so forgetting to override it fails loudly. The formal way: inherit from abc.ABC and mark methods @abstractmethod - then instantiating an incomplete child is impossible.

Abstract classesNotImplementedErrorabc module

Q25. hard

Why is 'type(x) == Dog' usually WORSE than 'isinstance(x, Dog)'?

class Puppy(Dog): pass
p = Puppy()
print(type(p) == Dog)        # False!
print(isinstance(p, Dog))    # True
  1. A type() ignores inheritance - a Puppy(Dog) instance would FAIL the type check but pass isinstance
  2. B type() is slower
  3. C type() doesn't exist
  4. D They're always identical

Answer: A — type() ignores inheritance - a Puppy(Dog) instance would FAIL the type check but pass isinstance

Exact-type checks break polymorphism: code demanding type(x) == Dog rejects perfectly good subclasses. isinstance accepts the whole family - almost always what you want.

isinstance()Liskov principle

Q28. hard

What is the 'diamond problem', and how does Python cope?

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)   # D, B, C, A, object
  1. A D inherits from B and C which both inherit A - Python's MRO (C3 linearization) gives ONE consistent lookup order, each class visited once
  2. B A crash when classes form a square
  3. C Python forbids the diamond shape entirely
  4. D Methods run twice

Answer: A — D inherits from B and C which both inherit A - Python's MRO (C3 linearization) gives ONE consistent lookup order, each class visited once

The diamond: D(B, C), B(A), C(A). Which A-method does D see, and does A run twice? Python's C3 algorithm computes a deterministic order (D, B, C, A) and super() follows it - so cooperative calls visit each class exactly once.

Diamond problemMROC3 linearization

Practice “Inheritance & Polymorphism” as a quiz