多重継承

Pythonでは、多重継承はクラスが複数の親クラスから属性やメソッドを継承できる機能です。これにより、複数のクラスの振る舞いや属性を組み合わせたクラスを作成することが可能で、とても強力です。しかし、その強力さゆえに複雑さも伴うため、多重継承は慎重に使用するべきです。
多重継承を理解するために、マルチメディアファイルシステムの例を考えてみましょう。VideoクラスとAudioクラスがあるとします。ここで、ビデオとオーディオの両方を持つマルチメディアファイルのクラスを作成したいとします。これを実現するには、多重継承を使用できます:
class Video:                               # 'Video'という名前のクラスを定義
    def __init__(self):
        self.video_codec = 'H.264'

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

class Audio:                               # 'Audio'という名前のクラスを定義
    def __init__(self):
        self.audio_codec = 'AAC'

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

class Multimedia(Video, Audio):            # 'Video'と'Audio'を継承する'Multimedia'というサブクラスを定義
    def __init__(self):
        Video.__init__(self)               # 親クラス'Video'の'__init__'メソッドを呼び出す
        Audio.__init__(self)               # 親クラス'Audio'の'__init__'メソッドを呼び出す

    def play(self):
        return self.play_video() + ' ' + self.play_audio()   # ビデオと音声の両方を再生

multimedia = Multimedia()                  # 'Multimedia'クラスのインスタンスを作成
print(multimedia.play())                   # Output: 'Playing video... Playing audio...'
この例では、MultimediaクラスはVideoクラスとAudioクラスの両方を継承しています。Multimediaのコンストラクタ内でVideoAudioのコンストラクタを呼び出し、Multimediaインスタンスのすべての属性を適切に初期化しています。Multimediaクラスのplay()メソッドは、Videoplay_video()メソッドとAudioplay_audio()メソッドを組み合わせています。
多重継承は複数のクラスからの振る舞いを組み合わせる方法を提供します。しかし、複雑な状況を引き起こす可能性があるため、注意深く使用する必要があります。
🤔
もし親クラスが同じ名前のメソッドを持っている場合、継承の順序が重要になります。Pythonはサブクラス定義でリストされている親クラスの順にメソッドを検索するためです。

メソッド解決順序

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'
    pass

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.
PythonはC3リニアリゼーション、または単にC3と呼ばれるアルゴリズムを使用して、クラスが複数の親から継承するときのメソッド解決順序を決定します。このアイデアは、サブクラスの宣言で定義されたクラスの順序を保持しつつ、親クラスからの順序も維持することです。しかし、アルゴリズムの複雑さに深入りせずに、クラスのMROはmro()メソッドを使って簡単に確認できます:

挑戦:Frogクラス

プログラミングの魔法の国には、いくつかの動物クラスがあります:FishWaterAnimal、そしてLandAnimal
  • FishWaterAnimalを継承しており、swim()というメソッドを持っています。
  • WaterAnimallive_in_water()というメソッドを持っています。
  • LandAnimallive_on_land()というメソッドを持っています。
あなたのタスクは、WaterAnimalLandAnimalを継承するFrogクラスを作成することです。このクラスは親クラスのすべてのメソッドを呼び出すことができるようにする必要があります。
各クラスは、オブジェクトが作成される際に指定されるspeciesという属性を持つべきです。
すべてのメソッドは、呼び出されたときに動作と種名を示す適切なメッセージを出力する必要があります(フォーマットは以下の例を参照)。
入力
出力
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.
 

Constraints

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