Pythonの複数クラス継承をJuliaで書いてみる

PythonコードをJuliaに移植するときに役に立ちそうなことをまとめておきます。
この記事は
"PythonっぽいJulia：PythonコードのclassをJuliaコードへ移植する"
https://qiita.com/cometscome_phys/items/0cc0ab0b43e1bb48a1ea
の続きとなっています。
今回は、Pythonにおけるクラス継承、特に、多重クラス継承が行われているコードをJuliaに移植してみます。
なお、これが最適かどうかはよくわかっていませんので、コメント等歓迎です。

# バージョン
Julia 1.1
Python 3系

# Pythonの複数クラス継承
"[Python]クラス継承(super)"
https://qiita.com/Kodaira_/items/42dfe18c81af98bf0db3
の記事にあるPythonコードをJuliaコードに移植してみることとします。

# 一つのクラスの継承
## Python
Creatureクラスというクラスには能力値が入っており、このCreatureクラスをもとに、Warrior、Magicianクラスを作っています。
WarriorとMagicianは一部の能力値が異なっています。

以下は(https://qiita.com/Kodaira_/items/42dfe18c81af98bf0db3) からの引用です。
引用始まり：

```python
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"
```
：引用終わり

これを実行すると、

```python
>>> 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
```
となります。

## Julia
次に、Juliaのコードです。

まず、abstract typeとしてCreatureタイプを作ります。

```julia
abstract type Creature
end
```
これはabstract typeなのでフィールドを持てません。そこで、別のmutable structを作ります。


```julia
mutable struct Creature_values
    level
    hp
    mp
    attack
    defence
    weapon
    job
    Creature_values(level,weapon=nothing)= new(level,0,0,0,0,weapon,"neet")
end
```
そして、Creatureタイプの子のタイプであるWarriorやMagicianは、このCreature_valuesタイプをフィールドに持つことにしましょう。

```julia
mutable struct Warrior <: Creature
    creature::Creature_values
end

mutable struct Magician <: Creature
    creature::Creature_values
end
```
Creatureタイプを親に持つタイプに対して、

```julia
function status(self::Creature)
    return "Job:$(self.creature.job) | HP:$(self.creature.hp) | MP:$(self.creature.mp) | Atk:$(self.creature.attack) | Def:$(self.creature.defence) | Weapon:$(self.creature.weapon) \n"
end
```
という関数を定義しておけば、WarriorタイプもMagicianタイプもこの関数を利用することができます。

次に、それぞれのタイプが持つフィールドの初期値を設定しなければなりません。
また、MagicianかつWarriorのようなタイプを今後作ることを考えると、転職して能力が増加するようにしたいところです。
これは、タイプを作る際にoptional変数を入れておけば実現できます。

```julia
mutable struct Warrior <: Creature
    creature::Creature_values

    function Warrior(level,weopon=nothing,creature::Creature_values = Creature_values(level,weopon)) 
        creature.attack += 3*level
        if creature.weapon == nothing
            creature.weapon = "sword"
        end
        if creature.job == "neet"
            creature.job = "Warrior"
        else
            creature.job *= "Warrior"
        end
        new(creature)
    end
end

mutable struct Magician <: Creature
    creature::Creature_values
    function Magician(level,weopon=nothing,creature::Creature_values = Creature_values(level,weopon)) 
        creature.mp += 4*level
        if creature.weapon == nothing
            creature.weapon = "rod"
        end
        if creature.job == "neet"
            creature.job = "Magic"
        else
            creature.job *= "Magic"
        end
        new(creature)
    end
end
```
Warrior,Magicianタイプは一つの引数で作成するとlevelを入力することになります。
また、初期装備を変更することもあるので、weoponをoptional変数にしました。
そして、Warriorタイプの初期値として、Creature_valuesタイプのcreatureをoptional変数としました。
これにより、

```julia
print(status(Warrior(5)))
print(status(Magician(5)))
print(status(Warrior(5,"katana")))
```
をすると、

```
Job:Warrior | HP:0 | MP:0 | Atk:15 | Def:0 | Weapon:sword 
Job:Magic | HP:0 | MP:20 | Atk:0 | Def:0 | Weapon:rod 
Job:Warrior | HP:0 | MP:0 | Atk:15 | Def:0 | Weapon:katana 
```
と出力されます。

#複数クラスの継承
# Python
次に、複数クラスの継承を行なって、MagicWarriorなどを作ってみましょう。

以下は(https://qiita.com/Kodaira_/items/42dfe18c81af98bf0db3) からの引用です。
引用始まり：

```python
class MagicWarrior(Warrior, Magician):
    def __init__(self, level):
        super().__init__(level)


class WarriorMagic(Magician, Warrior):
    def __init__(self, level):
        super().__init__(level)
```
：引用終わり

この二つのクラスはそれぞれWarriorとMagicianを継承していますが、順番が異なっています。
これを実行すると、

```python
>>> 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
```
となります。MagicWarriorかWarriorMagicで初期化の順番が異なっていることがわかると思います。

##Julia
上のような、二つのタイプを合わせ持つようなタイプを作るにはどうすれば良いでしょうか？

実は、上で書いたJuliaのコードではタイプの初期値の設定が可能になっているために、意外と簡単に実装することができます。
実装は、

```julia
mutable struct MagicWarrior <: Creature
    creature::Creature_values
    function MagicWarrior(level,weopon=nothing)
        magicwarrior = Warrior(level,weopon,Magician(level,weopon).creature)
        new(magicwarrior.creature)        
    end
end

mutable struct WarriorMagic <: Creature
    creature::Creature_values
    function WarriorMagic(level,weopon=nothing)
        warriormagic = Magician(level,weopon,Warrior(level,weopon).creature)
        new(warriormagic.creature)        
    end
end
```
です。MagicWarriorタイプでは、```Warrior(level,weopon,Magician(level,weopon).creature)```で、Magicianタイプの初期値を持つWarriorタイプ、というものを定義することができています。つまり、MagicianがWarriorに転職した、ということですね。
タイプを継承するのではなく、その中身であるcreatureを持ってきていることになります（移譲、というのでしょうか）。
WarriorMagicでは、Warriorタイプの初期値を持つMagicianタイプを定義しています。これは、WarriorがMagicianに転職した、ということになります。
実際、

```julia
print(status(MagicWarrior(5)))
print(status(WarriorMagic(5)))
```
実行してみると、

```
Job:MagicWarrior | HP:0 | MP:20 | Atk:15 | Def:0 | Weapon:rod 
Job:WarriorMagic | HP:0 | MP:20 | Atk:15 | Def:0 | Weapon:sword 
```
となっており、転職して新しいメイン職を得たようになっています。

なお、

```julia
print(status(MagicWarrior(5,"body")))
```
は、

```
Job:MagicWarrior | HP:0 | MP:20 | Atk:15 | Def:0 | Weapon:body 
```
となっており、装備品の変更も可能です。

#まとめ
以上をまとめますと、Juliaの場合には、メソッドは親タイプに対して定義することで共通に使うことができ、共通の値に対しては、「タイプを継承する」のではなく、そのフィールドの値を初期値として持ってくること（移譲？）で、Pythonのクラス継承と似たようなことができるようです。