Multiple Inheritance

In Python, multiple inheritance is a feature where a class can inherit attributes and methods from more than one parent class. It allows you to create a class that combines behaviors or attributes from multiple other classes, which can be quite powerful. However, with power comes complexity, and multiple inheritance should be used judiciously.
To understand multiple inheritance, let's consider an example of a multimedia file system. Suppose we have a Video class and an Audio class. Now, we want to create a class for multimedia files that have both video and audio. We can achieve this using multiple inheritance:
class Video:                               # Define a class named 'Video'
    def __init__(self):
        self.video_codec = 'H.264'

    def play_video(self):
        return 'Playing video...'

class Audio:                               # Define a class named 'Audio'
    def __init__(self):
        self.audio_codec = 'AAC'

    def play_audio(self):
        return 'Playing audio...'

class Multimedia(Video, Audio):            # Define a subclass named 'Multimedia' that inherits from 'Video' and 'Audio'
    def __init__(self):
        Video.__init__(self)               # Call the parent 'Video' class's '__init__' method
        Audio.__init__(self)               # Call the parent 'Audio' class's '__init__' method

    def play(self):
        return self.play_video() + ' ' + self.play_audio()   # Play both video and audio

multimedia = Multimedia()                  # Create an instance of the 'Multimedia' class
print(                   # Output: 'Playing video... Playing audio...'
In this example, Multimedia class inherits from both Video and Audio classes. We call the constructors of the Video and Audio classes in the constructor of Multimedia to properly initialize all the attributes of the Multimedia instance. The play() method of the Multimedia class combines the play_video() method from Video and the play_audio() method from Audio.
Multiple inheritance provides a way to combine behaviors from multiple classes. However, it should be used carefully as it can lead to some complex situations.
If the parent classes have methods with the same names, the order of inheritance matters, since Python will search for methods in the order of the parent classes listed in the subclass definition. This is known as the Method Resolution Order (MRO).

Method Resolution Order

Python uses an algorithm called C3 linearization, or just C3, to determine the method resolution order when classes inherit from multiple parents. The idea is to preserve the order in which classes are defined in the subclass declaration, while also maintaining the order from the parent classes. However, without getting into the complexities of the algorithm, you can easily check the MRO for a class using the mro() method:
class A:                    # Define a class named 'A'
    def process(self):
        return 'A process'

class B(A):                 # Define a class named 'B' that inherits from 'A'
    def process(self):
        return 'B process'

class C(A):                 # Define a class named 'C' that inherits from 'A'
    def process(self):
        return 'C process'

class D(B, C):              # Define a class named 'D' that inherits from 'B' and 'C'

print(D.mro())              # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
In the example above, the MRO for class D is D -> B -> C -> A -> object. This means that when you call a method on an instance of D, Python first looks at the method in D. If it doesn't find it there, it moves on to B, then C, and then A.
If you want to call a specific parent class's method, you can directly call it using the parent class's name. However, this is generally not recommended because it can lead to code that is hard to maintain.
Let's modify class D to call the process method of class A:
class D(B, C):                  # Define a class named 'D' that inherits from 'B' and 'C'
    def process(self):
        return A.process(self)

d = D()                         # Create an instance of the 'D' class
print(d.process())              # A process
In the example above, even though class D inherits from B and C (both of which have a process method), we specifically call the process method from class A. This ensures that no matter what the MRO is, class A's process method is used.
Notice how the self is passed explicitly as an argument. In Python, self is a reference to the current instance of the class and is used to access variables and methods associated with that instance. It's implicitly passed as the first argument to instance methods. When we're calling A.process(self), we're saying "Call the process method of class A on this specific instance (represented by self)". Even though the method is defined in A, it's being executed in the context of the instance of class D. This means it has access to any attributes or methods defined in class D or its parent classes. In short, passing self to A.process allows us to invoke the process method from A on the instance of D.
Multiple inheritance can be a powerful tool for creating versatile and reusable classes in your Python programs.

Challenge: The Frog Class

In a magical land of programming, there are several classes of animals: Fish, WaterAnimal, and LandAnimal.
  • Fish, which inherits from WaterAnimal and has a method swim().
  • WaterAnimal has a method live_in_water().
  • LandAnimal has a method live_on_land().
Your task is to create a class Frog which inherits from both WaterAnimal and LandAnimal. This class should be able to call all the methods of its parent classes.
Each class should have an attribute species which should be specified when an object is created.
All methods when invoked should print an appropriate message which states the action and species name (see the example below for the format).
goldfish=Fish('Goldfish'); goldfish.swim(); goldfish.live_in_water(); frog=Frog('Frog'); frog.live_in_water(); frog.live_on_land();
The Goldfish is swimming. The Goldfish is living in the water. The Frog is living in the water. The Frog is living on land.


Time limit: 2 seconds

Memory limit: 512 MB

Output limit: 1 MB

To check your solution you need to sign in
Sign in to continue