Working with Multiple Levels of Inheritance
Inheritance syntax places the parent class name in parentheses after the child class name.
Try it: Inheritance and Method Lookup
How a subclass inherits from its parent, how Python finds an attribute by walking the instance, then the class's method resolution order up to object, how overriding and super() work, why the same call does different things on different objects (polymorphism), and what isinstance / issubclass check.
How it works
- class Dog(Animal) gives Dog the MRO Dog → Animal → object; Puppy(Dog) gives Puppy → Dog → Animal → object.
- obj.name searches obj.__dict__, then each class of type(obj).__mro__ in order; the first class that defines the name wins, so a subclass method overrides the parent's.
- super().method() starts the search in the class after the one the current method was defined in, e.g. to run the parent's __init__.
- Calling the same method on a list of different objects runs each object's own version (polymorphism).
- isinstance(obj, C) is True when C is in type(obj).__mro__; if no class has the name, Python raises AttributeError.
Default run (63 steps): Ready: Animal, Dog, Cat and Puppy, then the statements you picked. Each attribute is searched along the class's MRO. … Finished. Printed: Rex the dog says Woof / Tom the cat says Meow / Bo the dog says Woof! / True / Puppy / Dog / Animal / object.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Single inheritance only (the lab's MRO code is Python's C3 algorithm, but no class here has two parents).
Loading the simulation…
One Object, Several Sources of Behavior
A child class does not start from nothing. It extends a parent class, automatically receiving the parent's variables and methods while also gaining the ability to define additional features. This means one child-class instance can combine inherited behavior with behavior introduced specifically by the child.
The relationship is one-way: the child inherits from the parent, but the parent does not inherit from the child.
Following the Class Hierarchy
Inheritance syntax places the parent class name in parentheses after the child class name. The form class ChildClass(ParentClass): tells Python that ChildClass extends ParentClass. The parent sits above the child in the hierarchy. The child receives the parent's variables and methods automatically, so an instance of the child can use inherited features as well as features defined directly in the child.
In the source example, CricketFan is the child class and PartyAnimal is the parent class. PartyAnimal provides name and party(). CricketFan adds points and six(). An instance named j therefore has access to all four features: the inherited name and party(), plus the child-defined points and six().
Initializing Parent and Child State
A child class often needs to initialize two groups of variables: the variables required by the parent and the new variables required by the child. The child class can use super() inside its __init__ method to call the parent class's __init__ method. This lets the parent complete its own setup before the child adds child-specific variables.
class PartyAnimal:
def __init__(self, name):
self.name = name
class CricketFan(PartyAnimal):
def __init__(self, name, points):
super().__init__(name)
self.points = points
def six(self):
pass- 1The parent initializer receives name and stores the inherited name variable.
- 2The child initializer calls
super().__init__(name) so the parent setup is performed. - 3After the parent initializer returns, the child stores its own points variable.
- 4The child also defines
six(), which is not supplied by the parent.
- PartyAnimal defines the parent initialization.
- CricketFan extends PartyAnimal.
super().__init__(name) calls the parent initializer.- points is added after the parent setup.
six()is a method defined by CricketFan.
Keeping Inherited Behavior and Adding More
class PartyAnimal:
def party(self):
passThe parent provides party().
class PartyAnimal:
def party(self):
pass
class CricketFan(PartyAnimal):
def six(self):
passThe child still has inherited party() and now also has six().
Extending a class does not remove the parent's functionality. CricketFan keeps access to the inherited party() method and adds its own six() method. The same combination applies to variables: name comes from the parent setup, while points is introduced by the child.
| Feature | Where it is defined | Available on CricketFan instance |
|---|---|---|
| name | PartyAnimal | Yes, inherited |
| party() | PartyAnimal | Yes, inherited |
| points | CricketFan | Yes, child-defined |
| six() | CricketFan | Yes, child-defined |
The source example separates inherited features from features added by CricketFan.
Tracing the CricketFan instance
Determine which features are available on an instance j of CricketFan.
- 1Identify the parent
CricketFan extends PartyAnimal, so PartyAnimal is the source of inherited features.
- 2Collect inherited features
The instance receives name and
party()from PartyAnimal. - 3Collect child features
CricketFan adds points and
six(). - 4Combine the results
The child instance can access both groups of features.
j has name and party() from PartyAnimal, plus points and six() from CricketFan.
Mistakes in Parent Initialization
Common mistake Treating the child as if it receives only its own variables and methods.
What it looks likeAssuming a CricketFan instance has points and six() but not name and party().
Why it failsA child instance contains inherited variables and methods as well as child-defined features.
Do this insteadCheck both the parent and child class when listing what the instance can access.
Common mistake Defining the child without naming its parent in parentheses.
What it looks likeWriting a child class without the relationship represented by class ChildClass(ParentClass):.
Why it failsThe parenthesized class name tells Python which class the new class extends.
Do this insteadPlace the parent class name in parentheses after the child class name.
Common mistake Setting child variables before calling the parent initializer.
What it looks likeAssigning the child-specific state before super().__init__().Why it failsThe source guidance requires the parent to be fully initialized first.
Do this insteadCall super().__init__() before setting up child-specific variables.Common mistake Expecting the parent to inherit child features.
What it looks likeAssuming PartyAnimal can use the points variable or six() defined by CricketFan.
Why it failsInheritance moves from parent to child, not from child to parent.
Do this insteadTreat the relationship as one-way: the child receives parent features.
Practice the Inheritance Trace
Suppose Player is a parent class with a name variable and a play() method. Suppose CricketPlayer extends Player and adds a points variable and a score() method. List the features an instance of CricketPlayer should contain, and state which class provides each feature.
Hints
- Separate features defined by the parent from features added by the child.
- Remember that the child instance combines inherited and child-defined features.
- Use the PartyAnimal and CricketFan example as a model.
Explain why a child initializer should call super().__init__() before assigning its own variables. In your explanation, identify what setup belongs to the parent and what setup belongs to the child.
Hints
- The parent initializer prepares the parent class's variables.
- The child initializer can add new variables after the parent setup is complete.
Key Takeaways
- 1Use class
ChildClass(ParentClass): to define a child that extends a parent. - 2A child inherits the parent's variables and methods automatically.
- 3Use
super().__init__() inside the child initializer to run the parent initializer. - 4Call the parent initializer before setting child-specific variables.
- 5A child instance combines inherited features with new variables and methods defined by the child.
At a glance
- Inheritance creates a one-way parent-to-child relationship.
- The child class receives the parent's variables and methods and can add its own.
- The
super()function lets the child call the parent class's__init__method. - Parent initialization should happen before child-specific setup.
- An instance of the child class combines inherited and newly defined functionality.