Edited at

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