Help us understand the problem. What is going on with this article?

[Python]クラス継承(super)

More than 1 year has passed since last update.

クラス継承

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

一つのクラスの継承

ここは,単純に,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を使った際に呼び出されるのは後ろから

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away