Python 3のクラス継承を理解する

前書き

オブジェクト指向プログラミングは、再利用可能なコードのパターンを作成して、開発プロジェクトの冗長性を削減します。 1つのサブクラスが別の基本クラスのコードを活用できる場合、オブジェクト指向プログラミングがリサイクル可能なコードを実現する1つの方法は継承です。

このチュートリアルでは、親クラスと子クラスの動作、メソッドと属性のオーバーライド方法、super()関数の使用方法、多重継承の使用方法など、Pythonでの継承の主要な側面について説明します。 。

継承とは

Inheritanceは、クラスが別のクラス内で構築されたコードを使用する場合です。 生物学の観点から継承を考えると、子供が親から特定の特性を継承していると考えることができます。 つまり、子供は親の身長または目の色を継承できます。 また、子供は両親と同じ姓を共有する場合があります。

child classesまたはsubclassesと呼ばれるクラスは、parent classesまたはbase classesからメソッドと変数を継承します。

子クラスChildが継承するlast_nameheight、およびeye_colorclass variablesを持つParentという親クラスを考えることができます。 Parentから。

ChildサブクラスはParent基本クラスから継承しているため、ChildクラスはParentのコードを再利用でき、プログラマーはより少ないコード行を使用して冗長性を減らすことができます。 。

親クラス

親または基本クラスは、子またはサブクラスのベースとなるパターンを作成します。 親クラスを使用すると、毎回同じコードを書き直すことなく、継承を通じて子クラスを作成できます。 任意のクラスを親クラスにすることができるため、それらはそれぞれ単なるテンプレートではなく、それ自体が完全に機能するクラスです。

Personal_accountBusiness_accountの子クラスを持つ一般的なBank_account親クラスがあるとしましょう。 個人口座とビジネス口座の間の方法の多くは、お金を引き出したり預けたりする方法など、類似しているため、それらはBank_accountの親クラスに属することができます。 Business_accountサブクラスには、ビジネスレコードとフォームを収集する方法や、employee_identification_number変数など、それに固有のメソッドがあります。

同様に、Animalクラスにはeating()メソッドとsleeping()メソッドがあり、Snakeサブクラスには独自のhissing()メソッドとslithering()メソッドが含まれている場合があります。

後でサブクラスとして魚のタイプを構築するために使用するFish親クラスを作成しましょう。 これらの魚にはそれぞれ、特性に加えて名と姓があります。

fish.pyという名前の新しいファイルを作成し、__init__() constructor methodから始めます。このファイルには、各Fishオブジェクトのfirst_nameおよびlast_nameクラス変数が入力されます。またはサブクラス。

fish.py

class Fish:
    def __init__(self, first_name, last_name="Fish"):
        self.first_name = first_name
        self.last_name = last_name

ほとんどの魚がこれを姓として持つことがわかっているため、last_name変数を文字列"Fish"で初期化しました。

他のメソッドも追加しましょう。

fish.py

class Fish:
    def __init__(self, first_name, last_name="Fish"):
        self.first_name = first_name
        self.last_name = last_name

    def swim(self):
        print("The fish is swimming.")

    def swim_backwards(self):
        print("The fish can swim backwards.")

メソッドswim()swim_backwards()Fishクラスに追加したので、すべてのサブクラスもこれらのメソッドを利用できるようになります。

作成する魚のほとんどは、cartilaginous fish(軟骨で作られた骨格を持つ)ではなく、bony fish(骨で作られた骨格を持つ)と見なされるためです。 、__init__()メソッドにさらにいくつかの属性を追加できます。

fish.py

class Fish:
    def __init__(self, first_name, last_name="Fish",
                 skeleton="bone", eyelids=False):
        self.first_name = first_name
        self.last_name = last_name
        self.skeleton = skeleton
        self.eyelids = eyelids

    def swim(self):
        print("The fish is swimming.")

    def swim_backwards(self):
        print("The fish can swim backwards.")

親クラスを作成する方法は、他のクラスを作成する方法と同じです。ただし、子クラスで作成したメソッドを子クラスで使用できるようにすることを考えています。

子クラス

子またはサブクラスは、親クラスから継承するクラスです。 これは、各子クラスが親クラスのメソッドと変数を利用できることを意味します。

たとえば、Fishクラスをサブクラス化するGoldfish子クラスは、Fishで宣言されたswim()メソッドを宣言せずに使用できます。

各子クラスは親クラスのクラスと考えることができます。 つまり、Rhombusという子クラスとParallelogramという親クラスがある場合、Rhombusis aParallelogramと同じように言うことができます。 Goldfishis aFish

子クラスの最初の行は、親クラスを子クラスにパラメーターとして渡す必要があるため、非子クラスとは少し異なります。

class Trout(Fish):

Troutクラスは、Fishクラスの子です。 括弧内にFishという単語が含まれているため、これがわかります。

子クラスを使用すると、メソッドを追加するか、既存の親メソッドをオーバーライドするか、passキーワードを使用してデフォルトの親メソッドを受け入れるかを選択できます。この場合は次のようにします。

fish.py

...
class Trout(Fish):
    pass

これで、追加のメソッドを定義することなく、Troutオブジェクトを作成できます。

fish.py

...
class Trout(Fish):
    pass

terry = Trout("Terry")
print(terry.first_name + " " + terry.last_name)
print(terry.skeleton)
print(terry.eyelids)
terry.swim()
terry.swim_backwards()

Trout子クラスでメソッドを定義していなくても、Fishクラスの各メソッドを使用するTroutオブジェクトterryを作成しました。 他のすべての変数が初期化されたため、"Terry"の値をfirst_name変数に渡すだけで済みました。

プログラムを実行すると、次の出力が表示されます。

OutputTerry Fish
bone
False
The fish is swimming.
The fish can swim backwards.

次に、独自のメソッドを含む別の子クラスを作成しましょう。 このクラスをClownfishと呼びます。その特別なメソッドにより、イソギンチャクと一緒に暮らすことができます。

fish.py

...
class Clownfish(Fish):

    def live_with_anemone(self):
        print("The clownfish is coexisting with sea anemone.")

次に、Clownfishオブジェクトを作成して、これがどのように機能するかを確認しましょう。

fish.py

...
casey = Clownfish("Casey")
print(casey.first_name + " " + casey.last_name)
casey.swim()
casey.live_with_anemone()

プログラムを実行すると、次の出力が表示されます。

OutputCasey Fish
The fish is swimming.
The clownfish is coexisting with sea anemone.

出力は、ClownfishオブジェクトcaseyFishメソッド__init__()swim()、およびその子クラスメソッドlive_with_anemone()を使用できることを示しています。 s。

Troutオブジェクトでlive_with_anemone()メソッドを使用しようとすると、エラーが発生します。

Outputterry.live_with_anemone()
AttributeError: 'Trout' object has no attribute 'live_with_anemone'

これは、メソッドlive_with_anemone()Clownfishの子クラスにのみ属し、Fishの親クラスには属していないためです。

子クラスは、それが属する親クラスのメソッドを継承するため、各子クラスはプログラム内でこれらのメソッドを利用できます。

親メソッドのオーバーライド

これまで、passキーワードを使用してすべての親クラスFishの動作を継承する子クラスTroutと、別の子クラスClownfishを見てきました。すべての親クラスの動作を継承し、子クラスに固有の独自のメソッドも作成しました。 ただし、場合によっては、すべてではなく親クラスの動作の一部を使用することがあります。 親クラスのメソッドを変更するときは、それらをoverrideします。

親クラスと子クラスを構築するときは、オーバーライドによって不要なコードや冗長なコードが生成されないように、プログラムの設計に留意することが重要です。

Fish親クラスのShark子クラスを作成します。 主に硬骨魚を作成するという考えでFishクラスを作成したため、代わりに軟骨魚であるSharkクラスを調整する必要があります。 プログラム設計に関しては、複数の非骨魚がいる場合、これらの2種類の魚のそれぞれに個別のクラスを作成することをお勧めします。

サメは、骨の多い魚とは異なり、骨ではなく軟骨でできた骨格を持っています。 まぶたもあり、後方に泳ぐことができません。 ただし、サメは沈むことで後方に移動できます。

これに照らして、__init__()コンストラクターメソッドとswim_backwards()メソッドをオーバーライドします。 サメは泳ぐことができる魚なので、swim()メソッドを変更する必要はありません。 この子クラスを見てみましょう。

fish.py

...
class Shark(Fish):
    def __init__(self, first_name, last_name="Shark",
                 skeleton="cartilage", eyelids=True):
        self.first_name = first_name
        self.last_name = last_name
        self.skeleton = skeleton
        self.eyelids = eyelids

    def swim_backwards(self):
        print("The shark cannot swim backwards, but can sink backwards.")

__init__()メソッドで初期化されたパラメーターをオーバーライドしたため、last_name変数は文字列"Shark"と等しく設定され、skeleton変数は文字列"cartilage"であり、eyelids変数がブール値Trueに設定されるようになりました。 クラスの各インスタンスは、これらのパラメーターをオーバーライドすることもできます。

swim_backwards()メソッドは、Fishの親クラスの文字列とは異なる文字列を出力するようになりました。これは、サメが硬骨魚のように後方に泳ぐことができないためです。

これで、Shark子クラスのインスタンスを作成できます。これは、Fish親クラスのswim()メソッドを引き続き使用します。

fish.py

...
sammy = Shark("Sammy")
print(sammy.first_name + " " + sammy.last_name)
sammy.swim()
sammy.swim_backwards()
print(sammy.eyelids)
print(sammy.skeleton)

このコードを実行すると、次の出力が表示されます。

OutputSammy Shark
The fish is swimming.
The shark cannot swim backwards, but can sink backwards.
True
cartilage

Shark子クラスは、親クラスのswim()メソッドを継承しながら、Fish親クラスの__init__()およびswim_backwards()メソッドを正常にオーバーライドしました。

他のクラスよりもユニークな子クラスの数が限られている場合、親クラスのメソッドをオーバーライドすることが有用であることがわかります。

super()関数

super()関数を使用すると、クラスオブジェクトで上書きされた継承メソッドにアクセスできます。

super()関数を使用する場合、親メソッドを子メソッドに呼び出して使用します。 たとえば、親メソッドの特定の機能を特定の機能でオーバーライドした後、元の親メソッドの残りを呼び出してメソッドを終了したい場合があります。

学生を採点するプログラムでは、Gradeの親クラスから継承するWeighted_gradeの子クラスが必要になる場合があります。 子クラスWeighted_gradeでは、親クラスのcalculate_grade()メソッドをオーバーライドして、加重評点を計算する機能を含めながら、元のクラスの残りの機能を保持したい場合があります。 super()関数を呼び出すことにより、これを実現できます。

super()関数は、__init__()メソッド内で最も一般的に使用されます。これは、子クラスに一意性を追加してから、親からの初期化を完了する必要があるためです。

これがどのように機能するかを確認するために、Troutの子クラスを変更してみましょう。 マスは通常淡水魚なので、water変数を__init__()メソッドに追加し、文字列"freshwater"と同じに設定しますが、残りの親クラスの変数とパラメーターは維持します。

fish.py

...
class Trout(Fish):
    def __init__(self, water = "freshwater"):
        self.water = water
        super().__init__(self)
...

Trout子クラスの__init__()メソッドをオーバーライドし、親クラスFishによって既に定義されている__init__()の異なる実装を提供します。 Troutクラスの__init__()メソッド内で、Fishクラスの__init__()メソッドを明示的に呼び出しました。

メソッドをオーバーライドしたため、first_nameをパラメーターとしてTroutに渡す必要がなくなり、パラメーターを渡した場合は、代わりにfreshwaterをリセットします。 したがって、オブジェクトインスタンスで変数を呼び出すことにより、first_nameを初期化します。

これで、親クラスの初期化された変数を呼び出すことができ、一意の子変数も利用できます。 これをTroutのインスタンスで使用してみましょう。

fish.py

...
terry = Trout()

# Initialize first name
terry.first_name = "Terry"

# Use parent __init__() through super()
print(terry.first_name + " " + terry.last_name)
print(terry.eyelids)

# Use child __init__() override
print(terry.water)

# Use parent swim() method
terry.swim()
OutputTerry Fish
False
freshwater
The fish is swimming.

出力は、Trout子クラスのオブジェクトterryが、子固有の__init__()変数waterの両方を利用できると同時に、%を呼び出すことができることを示しています。 (t4)sの親__init__()変数のfirst_namelast_name、およびeyelids

組み込みのPython関数super()を使用すると、子クラスのメソッドの特定の側面をオーバーライドする場合でも、親クラスのメソッドを利用できます。

多重継承

Multiple inheritanceは、クラスが複数の親クラスから属性とメソッドを継承できる場合です。 これにより、プログラムは冗長性を減らすことができますが、ある程度の複雑さとあいまいさをもたらす可能性があるため、プログラム全体の設計を考慮して行う必要があります。

多重継承がどのように機能するかを示すために、CoralクラスとSea_anemoneクラスから継承するよりもCoral_reef子クラスを作成しましょう。 それぞれにメソッドを作成してから、Coral_reef子クラスでpassキーワードを使用できます。

coral_reef.py

class Coral:

    def community(self):
        print("Coral lives in a community.")


class Anemone:

    def protect_clownfish(self):
        print("The anemone is protecting the clownfish.")


class CoralReef(Coral, Anemone):
    pass

Coralクラスには1行を出力するcommunity()というメソッドがあり、Anemoneクラスには別の行を出力するprotect_clownfish()というメソッドがあります。 次に、両方のクラスを継承tupleに呼び出します。 これは、Coralが2つの親クラスから継承していることを意味します。

Coralオブジェクトをインスタンス化してみましょう。

coral_reef.py

...
great_barrier = CoralReef()
great_barrier.community()
great_barrier.protect_clownfish()

オブジェクトgreat_barrierCoralReefオブジェクトとして設定され、両方の親クラスのメソッドを使用できます。 プログラムを実行すると、次の出力が表示されます。

OutputCoral lives in a community.
The anemone is protecting the clownfish.

出力は、両方の親クラスのメソッドが子クラスで効果的に使用されたことを示しています。

多重継承により、子クラスの複数の親クラスのコードを使用できます。 同じメソッドが複数の親メソッドで定義されている場合、子クラスはそのタプルリストで宣言されている最初の親のメソッドを使用します。

多重継承は効果的に使用できますが、他のプログラマーが理解しづらくなり、プログラムがあいまいになったり困難になったりしないように、注意して多重継承を行う必要があります。

結論

このチュートリアルでは、親クラスと子クラスの構築、子クラス内の親メソッドと属性のオーバーライド、super()関数の使用、および子クラスが複数の親クラスから継承できるようにする方法について説明しました。

オブジェクト指向コーディングの継承により、ソフトウェア開発のDRY(繰り返さないでください)の原則を順守することができ、より少ないコードと繰り返しでより多くのことが可能になります。 継承はまた、コードが効果的で明確であることを保証するために、作成しているプログラムをどのように設計するかをプログラマーに考えるよう促します。