PythonっぽいJulia：PythonコードのclassをJuliaコードへ移植する

PythonのコードをJuliaのコードに移植したい！ということがあるかと思います。
その際、最初に困りそうなものはPythonにおけるclassの扱いです。
Pythonにclassがあったときに、どのようにJuliaに移植するか、ということを書きます。
なお、Pythonのclassについてはあまり詳しくないので、
"【Python入門】Pythonにおけるclassの使い方とは?"
https://qiita.com/Morio/items/0fe3abb58fcaff229f3d
と
"Pythonのイテレータとジェネレータ"
https://qiita.com/tomotaka_ito/items/35f3eb108f587022fa09
を参考にさせていただきました。


#バージョン
Julia 1.1
Python 3系

# class
## Python
まず初めに、Python3系で以下のようなclassがあったとします。

```python
class MyIterator(object):
    def __init__(self, *numbers):
        self._numbers = numbers
        self._i = 0

my_iterator = MyIterator(10, 20, 30)
print(my_iterator._numbers)
```
これを実行すると、

```
(10, 20, 30)
```
となります。Pythonでのclassの初期化は、```__init__```が使われます。

## Julia
次に、このコードと等価なJuliaコードは、

```julia
module myiterator
    export MyIterator

    struct MyIterator 
        _numbers
    end
    function MyIterator(numbers...)
        _numbers = numbers
        return MyIterator(_numbers)
    end
end

using .myiterator
my_iterator = MyIterator(10,20,30)
println(my_iterator._numbers)
```
となります。ここで、上のコードは```module```を使わずに

```julia
struct MyIterator 
    _numbers
end
function MyIterator(numbers...)
    _numbers = numbers
    return MyIterator(_numbers)                
end
my_iterator = MyIterator(10,20,30)
println(my_iterator._numbers)
```
としても構いません。Pythonのclassと同じように動作をひとまとめにするために、あえて```module```を使っています。
ここで、```MyIterator```型の```_numbers```は変更されないものと仮定していますが、もし値が変更されるようなものであれば、```struct```のかわりに```mutable struct```を使います。

さて、```MyIterator(numbers...)```の```numbers...```の```...```は同じような引数が複数来る場合を想定しています。この関数の中で```MyIterator```型を返り値とすることで、Pythonでの```__init__```と同等の機能が得られます。

# イテレータ
次に、forループをinで回すことを考えます。
## Python
"Pythonのイテレータとジェネレータ"
https://qiita.com/tomotaka_ito/items/35f3eb108f587022fa09
を参考にして、classをinを使ってfor文で回すことを考えます。コードは

```Python
class MyIterator(object):
    def __init__(self, *numbers):
        self._numbers = numbers
        self._i = 0
    def __iter__(self):
        # __next__()はselfが実装してるのでそのままselfを返す
        return self
    def __next__(self):  # Python2だと next(self) で定義
        if self._i == len(self._numbers):
            raise StopIteration()
        value = self._numbers[self._i]
        self._i += 1
        return value

my_iterator = MyIterator(10, 20, 30)
for num in my_iterator:
    print('hello %d' % num)
```
とします。
この出力結果は、

```
hello 10
hello 20
hello 30
```
となります。for文で回すためにイテレータというものを設定しており、このclassはiterableなオブジェクトとなっています。

## Julia
Juliaでのイテレータは
"Julia 0.7-DEV の新しい Iteration に触れてみた。"
https://qiita.com/antimon2/items/8b1a96d1370bb6252757
を参考にします。
コードは

```julia
module myiterator
    export MyIterator

    struct MyIterator 
        _numbers
    end
    function MyIterator(numbers...)
        _numbers = numbers
        return MyIterator(_numbers)
    end
    function Base.iterate(self::MyIterator,i::Int=0)
        i == length(self._numbers) && return nothing
        value = self._numbers[i+1]
        i +=1
        return (value,i)
    end
end

using .myiterator
my_iterator = MyIterator(10,20,30)
for num in my_iterator
    println("hello \t",num)
end
```
となります。```Base.iterate```を型```MyIterator```に対して多重定義しています。
```Base.iterate```は返り値としては二つの要素を持ち、なんらかの値と状態を表す値(この場合は変数i)となっています。これによって、```for num in my_iterator```のように、for文でinとして呼べるようになります。

#継承
次は、classの継承についてです。
## Python
Pythonのクラスは他のクラスを継承することができます。
今回は、
"【Python入門】Pythonにおけるclassの使い方とは?"
https://qiita.com/Morio/items/0fe3abb58fcaff229f3d
を参考にします。コードを


```Python
class Test:
    def __init__(self, num):
        self.num = num;

    def print_num(self):
        print('引数で渡された数字は{}です。'.format(self.num))

class Test2(Test):  #Testクラスを継承
    def print_test2_info(self):
        print('このクラスはTestクラスを継承しています。')
        super().print_num()  #親クラスのprint_num()を呼び出す

test = Test2(10)
test.print_test2_info()
```
とします。実行すると、

```
このクラスはTestクラスを継承しています。
引数で渡された数字は10です。
```
と出力されます。

クラス```Test```はメソッドとして```print_num```を持っており、初期化は```__init__```で行われます。クラス```Test2```はクラス```Test```を継承しているので、```print_num```を使うことができますし、初期化は```Test```の```__init__```を使うことができます。

## Julia
### 以下は間違いです。正しいものは追記に後述しました
~~Juliaでは、Pythonの継承に相当する機能を使うためには、抽象型Abstract typeを使います。
これは、Abstract type型は子のtypeを持つことができ、子のtypeは親のtypeが使用条件である関数を使うことができます。
したがって、コードは~~

```julia
#=
abstract type Test end
function Test(self::Test,num)
    return self(num)
end
function print_num(self::Test)
    println("引数で渡された数字は$(self.num)です。")    
end
struct Test2 <: Test
    num
end
function Test2(self::Test2,num)
    return Test(self,num)
end
function print_test2_info(self::Test2)
    println("この型はTest型を継承しています。")
    print_num(self)
end
test = Test2(10)
print_test2_info(test)
=#
```
~~となります。ここで、abstract typeとして```Test```を定義し、型が```Test```である引数がきた場合に使われる関数```print_num```が定義されています。ここで```self```と引数をしていますが、これはPythonと似せるためにそうしているだけで、どんなものでも構いません。
型```Test2```は```<: Test```となっており、```Test```をsupertypeとして持ちます。そのため、```Test```を引数とする関数を使うことができます。~~

~~Pythonのように```__init__```に相当するものを親と子で共通化する方法は存在するかわかりませんでした。上のコードでは、```Test```の初期化として```Test(self::Test,num)```を、```Test2```の初期化として```Test2(self::Test2,num)```を呼んでいまして、```Test2(self::Test2,num)```の中では```Test(self::Test,num)```を呼ぶことで共通の初期化となるようにしています。~~

~~ここで注意しなければならないのは、抽象型abstract typeはフィールドを持てない、ということです。ここでは、```num```です。
そのため、その型がどんなフィールドを持っているかは、型の葉であるmutable structやstructを見なければなりません。
しかしこれはわかりやすさの意味では良いかもしれません。というのは、実際に操作するのは```Test2```ですので、その定義を見ればどのようなフィールドがあるかを親を見ずにわかりますので。~~

### 追記
上記のコードは間違っていますので、下に新しいコードを記します。

Juliaでは、Pythonの継承に相当する機能を使うためには、抽象型Abstract typeを使います。
これは、Abstract type型は子のtypeを持つことができ、子のtypeは親のtypeが使用条件である関数を使うことができます。

```julia
abstract type Test end
function (test::Type{<:Test})(num)
    println("初期化")
    return test(num)
end
function print_num(self::Test)
    println("引数で渡された数字は$(self.num)です。")    
end
struct Test2 <: Test
    num
end

function print_test2_info(self::Test2)
    println("この型はTest型を継承しています。")
    print_num(self)    
end
test = Test2(10)
print_test2_info(test)
```

となります。ここで、abstract typeとして```Test```を定義し、型が```Test```である引数がきた場合に使われる関数```print_num```が定義されています。ここで```self```と引数をしていますが、これはPythonと似せるためにそうしているだけで、どんなものでも構いません。
型```Test2```は```<: Test```となっており、```Test```をsupertypeとして持ちます。そのため、```Test```を引数とする関数を使うことができます。

Pythonのように```__init__```に相当するものを親と子で共通化する方法は存在するかわかりませんでした。
@antimon2 さんのコメントにもありますように、抽象型abstract typeはフィールドを持てないために、これはできないようです。フィールドとは、ここでは```num```です。
しかし、親が同じ子が初期化するための方法を記述することはできるようです。上記コードでは

```julia
function (test::Type{<:Test})(num)
    println("初期化")
    return test(num)
end
```
の部分のことです。
これは型が```Test```に属するようなものをまとめて初期化するようにしています。これではあまり恩恵を感じられませんので、以下の節を見てください。

# 継承と初期化
## Python
Pythonコードが

```python
class Test:
    def __init__(self, num):
        self.a = 100
        self.num = num;

    def print_num(self):
        print('引数で渡された数字は{}です。'.format(self.num))
        print('aの値は{}です。'.format(self.a))        

class Test2(Test):  #Testクラスを継承
    def print_test2_info(self):
        print('このクラスはTestクラスを継承しています。')
        super().print_num()  #親クラスのprint_num()を呼び出す

test = Test2(10)
test.print_test2_info()
```
であるとします。このコードでは、```Test2```の```a```を100として初期化しています。
このように、他の値はデフォルトのままにしたい、という需要はあるかと思います。l

## Julia
上記のPythonコードをJuliaで書くと、

```julia
abstract type Test end
function (test::Type{<:Test})(num)
    println("初期化")
    a = 100
    return test(num,a)
end
function print_num(self::Test)
    println("引数で渡された数字は$(self.num)です。")    
    println("aの値は$(self.a)です。")
end
struct Test2 <: Test
    num
    a
end

function print_test2_info(self::Test2)
    println("この型はTest型を継承しています。")
    print_num(self)    
end
test = Test2(10)
print_test2_info(test)
```
となります。

```julia
function (test::Type{<:Test})(num)
    println("初期化")
    a = 100
    return test(num,a)
end
```
では、```Test```型を親にもつ型に対して動作を指定しています。
ただし、子のフィールドが二つでなければエラーとなります。
```Test2```はフィールドを二つ持っているので、エラーなしで動きました。
フィールドが二つである、とはっきりさせておきたい場合には、

```julia
function (test::Type{<:Test})(num)
    println("初期化")
    if length(test.types) == 2
        a = 100
        return test(num,a)
    elseif length(test.types) == 1
        return test(num)
    else
        println("Error! num. of fields should be 2")
    end
end
```
としても良いかと思います。



#まとめ
以上のように、PythonのclassはJuliaのtypeを使って書けることがわかりました。