Inheritance and Class Extension
super() is a special syntax that calls a method from the parent class. In the context of __init__, super().__init__() ensures the parent class's initialization runs before the child class's initialization.
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 Instance, Two Initializers
When a child class extends a parent class, the child instance can contain attributes initialized by both classes. The child class adds its own setup, but the parent class's setup does not happen automatically inside the child's __init__ method. The child initializer must use super().__init__() to call the parent initializer.
The Delegation Path
class Account:
def __init__(self, owner):
self.owner = owner
class SavingsAccount(Account):
def __init__(self, owner, interest_rate):
super().__init__(owner)
self.interest_rate = interest_rate- 1Creating a SavingsAccount begins the child class's
__init__method. - 2
super().__init__(owner) delegates to Account.__init__. - 3The parent initializer sets owner.
- 4Control returns to SavingsAccount.
__init__, which setsinterest_rate.
- Account defines the parent initialization.
- SavingsAccount extends Account.
super().__init__(owner) calls the parent initializer with the required argument.- The child then initializes its own attribute.
Creation Order
The initialization order is deliberate. First, the child class's __init__ method begins. Next, super().__init__() calls the parent class's initialization method with the correct arguments. The parent initializer sets the parent attributes. Finally, execution returns to the child initializer, which sets the child-specific attributes. Calling super().__init__() at the beginning of the child initializer keeps this order explicit and ensures complete instance setup.
Tracing a SavingsAccount
Determine which attributes are initialized when a SavingsAccount is created.
- 1Child initialization starts
The SavingsAccount initializer receives owner and
interest_rate. - 2Parent initialization runs
super().__init__(owner) calls Account.__init__, which sets the owner attribute. - 3Child initialization resumes
After the parent initializer returns, the child sets
interest_rate.
The resulting child instance contains the parent attribute owner and the child attribute interest_rate.
Combined Instance State
A child class instance inherits the parent class's attributes and methods and adds its own. In the generated example, owner comes from the parent initializer and interest_rate comes from the child initializer. These attributes coexist in the same child instance; they are not separate objects created by the two classes.
Place super().__init__() at the beginning of the child class's __init__ method. Pass the arguments required by the parent initializer. This allows the parent attributes to be initialized before the child-specific attributes are set.
When the Delegation Is Missing
class Account:
def __init__(self, owner):
self.owner = owner
class SavingsAccount(Account):
def __init__(self, owner, interest_rate):
self.interest_rate = interest_rateThe child initializer sets only the child-specific attribute.
class Account:
def __init__(self, owner):
self.owner = owner
class SavingsAccount(Account):
def __init__(self, owner, interest_rate):
super().__init__(owner)
self.interest_rate = interest_rateThe parent initializer runs first, then the child-specific attribute is set.
Common mistake Omitting super().__init__() from the child initializer.
What it looks likeThe child sets interest_rate but never calls the parent initializer.
Why it failsThe parent attributes are not initialized by the parent initializer.
Do this insteadCall super().__init__() at the beginning of the child initializer.
Common mistake Calling super().__init__() without the required parent arguments.
What it looks likeThe parent initializer requires owner, but the child call does not provide it.
Why it failsThe parent initializer needs the correct arguments to initialize its attributes.
Do this insteadPass the arguments required by the parent initializer.
Common mistake Assuming that child-specific setup replaces parent setup.
What it looks likeThe child initializer sets its own attributes and treats that as complete initialization.
Why it failsA child instance inherits parent attributes and methods as well as adding its own.
Do this insteadUse super().__init__() so both parent and child initialization take place.
Checking Inherited Members
A child class inherits all attributes and methods from its parent class and adds its own. The source pack identifies dir() as one way to verify the members available through the child class. Use it as an inspection tool when you want to examine what the child class provides through inheritance and extension.
In this example, the parent initializer supplies owner, while the child initializer supplies interest_rate. The call to dir(account) can be used to inspect the attributes and methods available through the resulting child instance.
Practice the Trace
Create a parent class named Device with an __init__ method that initializes model. Then create a child class named Phone whose __init__ method accepts model and operating_system. Use super().__init__(model) before initializing operating_system. Finally, describe the order in which the two initializers run and list the attributes in one Phone instance.
Hints
- The parent initializer must receive the model argument.
- Place
super().__init__(model) at the beginning of Phone.__init__. - The completed instance should contain one parent attribute and one child attribute.
- 1Identify the parent class and the child class.
- 2Find the child initializer's
super().__init__() call. - 3Check that the arguments passed to
super().__init__() match the parent initializer's needs. - 4Trace the parent attribute initialization before the child attribute initialization.
- 5Confirm that both sets of attributes belong to the one child instance.
Key Takeaways
- 1
super()calls a method from the parent class. - 2
super().__init__() runs the parent initialization method from the child initializer. - 3The parent initializer should run before child-specific attributes are set.
- 4A child instance can contain both parent attributes and child attributes.
- 5
dir()can be used to inspect attributes and methods available through the child instance.
At a glance
super()provides the delegation from a child class to its parent class.super().__init__() ensures that parent attributes are initialized before child-specific attributes.- A child instance combines the attributes and methods inherited from the parent with the additions made by the child.
- The safest initialization pattern is to call
super().__init__() at the beginning of the child initializer with the correct arguments.