[Python]クラス継承(super)

  • 24
    いいね
  • 0
    コメント

クラス継承

名前の通り,基底クラスの機能を持った派生クラスを作ります.
*途中で力尽きたので多分加筆します

一つのクラスの継承

ここは,単純に,Creatureクラスを元に,Warrior, Magicianクラスを作ってます.
初期levelに応じて,能力を上昇させています.

status(self)関数では,パラメータの列挙を行っています.(テスト用)
__init__ はインスタンス生成の際に実行されます.
Worrior, Magicianクラスでは,武器をもたせたり,職業名の変更を行っています.

clas_test1.py
class Creature(object):
    def __init__(self, level=1, weapon=None):
        self.level = level
        self.hp = 0
        self.mp = 0
        self.attack = 0
        self.defence = 0
        self.weapon = weapon
        self.job = "neet"

    def status(self):
        return "Job:{} | HP:{} | MP:{} | Atk:{} | Def:{} | Weapon:{}".format \
                (self.job, self.hp, self.mp, self.attack, self.defence, self.weapon)


class Warrior(Creature):
    def __init__(self, level):
        super().__init__(level)
        self.attack += 3 * level
        if self.weapon is None:
            self.weapon = "sword"
        if self.job == "neet":
            self.job = "Warrior"
        else: self.job += "Warrior"


class Magician(Creature):
    def __init__(self, level):
        super().__init__(level)
        self.mp += 4 * level
        if self.weapon is None:
            self.weapon = "rod"
        if self.job == "neet":
            self.job = "Magic"
        else: self.job += "Magic"

下のようにlevelに応じて能力の上昇,基底クラスのstatus関数が使えています.

test1.py
>>> print(Warrior(5).status())
Job:Warrior | HP:0 | MP:0 | Atk:15 | Def:0 | Weapon:sword
>>> print(Magician(5).status())
Job:Magic | HP:0 | MP:20 | Atk:0 | Def:0 | Weapon:rod

複数クラス継承

複数クラスの場合も先ほどとやることはほとんど同じです.

class_test2.py
class MagicWarrior(Warrior, Magician):
    def __init__(self, level):
        super().__init__(level)


class WarriorMagic(Magician, Warrior):
    def __init__(self, level):
        super().__init__(level)

ここで,注意する点は,下の出力でわかるように,
super()を用いた時__init__が呼ばれる順番が後ろからになります.
この順番は,mro()で確認ができます.

test2.py
>>> print(MagicWarrior(5).status())
Job:MagicWarrior | HP:0 | MP:20 | Atk:15 | Def:0 | Weapon:rod
>>> print(WarriorMagic(5).status())
Job:WarriorMagic | HP:0 | MP:20 | Atk:15 | Def:0 | Weapon:sword
>>>
>>> WarriorMagic.mro()
[<class 'class_test.WarriorMagic'>, <class 'class_test.Magician'>, <class 'class_test.Warrior'>, <class 'class_test.Creature'>, <class 'object'>]

superを使わないとどうなるか,

例えば,

class_test3.py
class WarriorMagic(Magician, Warrior):
    def __init__(self, level):
        # super().__init__(level)
        Warrior.__init__(self, level)
        Magician.__init__(self, level)

一見,同じように見えるのですが,initの呼び出しの回数が違います.
この場合,Warrior > Creature, Magician > Warrior > Creature
といった感じで呼び出しています.

superを使った場合では,Magician > Warrior > Creature
と呼び出し回数が違います.

Job:WarriorMagic | HP:0 | MP:20 | Atk:15 | Def:0 | Weapon:sword

さらに,基底クラスのWarriorとMagicianクラスもsuperを使わずに定義すると

class_test4.py
class Warrior(Creature):
    def __init__(self, level):
        # super().__init__(level)
        Creature.__init__(self, level)
        self.attack += 3 * level
        if self.weapon is None:
            self.weapon = "sword"
        if self.job == "neet":
            self.job = "Warrior"
        else: self.job += "Warrior"


class Magician(Creature):
    def __init__(self, level):
        # super().__init__(level)
        Creature.__init__(self, level)
        self.mp += 4 * level
        if self.weapon is None:
            self.weapon = "rod"
        if self.job == "neet":
            self.job = "Magic"
        else: self.job += "Magic"
test4.py
Job:Magic | HP:0 | MP:20 | Atk:0 | Def:0 | Weapon:rod

こうすると,せっかく最初のWarriorで能力とかをつけたのに,
Magicianで2回目のinitを呼び出したせいで,パラメタが初期化され,ただのMagicianの能力しか割り振られていません.
その結果,下のようになってしまいます.

流れ的には,
Warrior > Creature, Magician > Creature
のような感じで呼び出されていて,見てわかる通りCreatureを2回呼び出されていて,先ほどとは違い,2回目にWarriorが呼び出されていないので,Worriorのinitが反映されない形になってしまってます.

まとめ

とりあえず,継承する際はsuperを基本的に使う
superを使った際に呼び出されるのは後ろから