Python クラスについて


#クラス
ではpythonでのクラスを見ていきます。

##定義方法
クラス定義の最も簡単な形は、次のようになります

```py:Python
class TestClass:
    pass
```

これで中身の何もないTestClassが作成されました。
利用したい場合は、関数を実行するときと同じように「()」を後ろにつけてインスタンスを生成します。

```py:Python
class TestClass:
    pass

x = TestClass()
```

これで変数xにTestClassのインスタンスが代入されました。


##メソッド
クラスに関数を宣言する方法を説明します。クラス内の関数をメソッドと呼びます。
メソッドの宣言は、クラス外と同じようにdefを使用して宣言します。
クラス外で宣言した場合と異なる点は、第一引数にそのクラスのインスタンスを表すオブジェクトを受け取ります。
そのオブジェクト名は、慣習で「self」という名称になっています。

```py:Python
class TestClass:
    x = "変数1"

    def test_method1(self):
        print(self.x)

    def test_method2(self, arg1):
        print("引数1:" + arg1)

testClass = TestClass()
testClass.test_method1() # ⇒ 変数1
testClass.test_method2("引数Test") # ⇒ 引数1:引数Test
```

このようにクラス内に定義されたメソッドを呼び出すことができます。
selfからはクラス内で定義した変数やメソッドを呼び出せます。

###オーバーロード
試したかったのでやってみましたが、pythonではオーバーロード（引数違いのメソッドを定義する）はできないようです。
エラーとなりました。
ただし、同じ名前のメソッドを２つ定義することはできるようで、その場合は、最後に定義したメソッドが有効になるようです。

```py:Python
class TestClass:

    def test_method2(self, arg1):
        print("引数1:" + arg1)

    def test_method2(self, arg1, arg2):
        print("引数1:" + arg1 + " 引数2:" + arg2)


testClass = TestClass()
testClass.test_method2("引数Test") # ⇒ TypeError: test_method2() missing 1 required positional argument: 'arg2'
testClass.test_method2("引数Test1", "引数Test2") # ⇒ 引数1:引数Test1 引数2:引数Test2
```


##コンストラクタ
コンストラクタは、インスタンスを生成したときに一度だけ呼び出されるメソッドです。
基本的には対象のクラスのインスタンスを初期化するために利用します。
コンストラクタを定義するには、「__init__」という特殊な名前でメソッドを定義する必要があります。
するとインスタンス作成時にこのメソッドが呼び出されます。

```py:Python
class TestClass2:
    val = []
    def __init__(self):
        print("init:" + str(self.val))
        # 初期化
        self.val.append(1)
        self.val.append(2)
        self.val.append(3)

    def test_method1(self):
        print("test_method2:" + str(self.val))

testClass2 = TestClass2()
testClass2.test_method1()

# > init:[]
# > test_method2:[1, 2, 3]
```

当然コンストラクタに引数を渡すこともできます。

```py:Python
class TestClass3:
    val = []
    def __init__(self, val1, val2):
        # 初期化
        self.val.append(val1)
        self.val.append(val2)
        print("init:" + str(self.val))

testClass3 = TestClass3(1, 2)

# > init:[1, 2]
```

コンストラクタは必ずしも定義しないといけないというわけではありません。
その場合は、何もしないコンストラクタが定義されているのと同義です。


##デストラクタ
デストラクタとは、コンストラクタとは反対にインスタンスをPythonが削除するときに動作するメソッドです。
デストラクタを定義するには、「__del__」という特殊な名前でメソッドを定義する必要があります。
するとインスタンス削除時にこのメソッドが呼び出されます。

```py:Python
class TestClass4:
    val = []
    def __del__(self):
        # デストラクタ
        print("del:デストラクタ")

testClass4 = TestClass4()
del testClass4
# > del:デストラクタ
```


##変数
pythonのクラス内に定義できる変数について説明します。
pythonのクラスで定義できる変数には、２種類あります。
１つは、クラス変数で、もう１つはインスタンス変数です。
クラス変数は、クラスに依存している変数です。インスタンスからでも、クラスからでもどちらからでも呼び出し可能で、インスタンスが異なっても同じ値を取得することができます。
javaでいう「public static」な変数と同じ。
インスタンス変数は、インスタンスに依存している変数です。インスタンスを複数作成すればそれぞれ異なる値をもちます。
javaでいう「public」な変数と同じ。
さっそく例を記述します。

```py:Python
class TestClass5:
    val = "クラス変数"
    def __init__(self, arg):
        self.x = arg

testClass5_1 = TestClass5("インスタンス変数1")
testClass5_2 = TestClass5("インスタンス変数2")
print(TestClass5.val) # > クラス変数
print(testClass5_1.x) # > インスタンス変数1
print(testClass5_2.x) # > インスタンス変数2
print(testClass5_1.val) # > クラス変数
```

クラス変数とインスタンス変数が同じ変数名だったらどうなるでしょうか。

```py:Python
class TestClass6:
    val = "クラス変数"
    def __init__(self, arg):
        self.val = arg

testClass6 = TestClass6("インスタンス変数1")
print(TestClass6.val) # > クラス変数
print(testClass6.val) # > インスタンス変数1
del testClass6.val
print(testClass6.val) # > クラス変数
```

このようにクラス変数とインスタンス変数が同じ変数名だった場合、インスタンス変数が優先されます。

ちなみにpythonではprivateな変数、変更不可な変数は存在しないようです。
ただ、privateに関しては、「_」で始まる変数、メソッドはプライベートだという慣習はあるみたいです。
普通に呼び出すことは可能なので個人の努力が必要ですが。。。

##継承
継承とは、他のクラスと同じインスタンス変数、メソッドをもつクラスを定義できる機能です。
この機能により似た機能を持ったクラスをより効率的に作成することができます。
継承を行うには、クラス定義のときにクラス名の後ろに「(継承するクラス名)」とつけて定義することでできます。
では、例を見ていきましょう。

```py:Python
class Parent:
    parent_val = "Parent"

    def parent_function(self):
        print("ParentMethod:" + self.parent_val)

class Child(Parent):
    child_val = "Child"

    def child_function(self):
        print("ChildMethod:" + self.child_val)

child = Child()
print(child.parent_val) # ⇒ Parent
print(child.child_val) # ⇒ Child
child.parent_function() # ⇒ ParentMethod:Parent
child.child_function() # ⇒ ChildMethod:Child
```

この例では、クラス「Parent」を継承したクラス「Child」を定義しています。
作成したChildインスタンスでは、Childクラスの変数、メソッドだけでなくParentクラスの変数、メソッドも使用できることが確認できます。
当然、Childクラスのメソッド内からもParentクラスの要素を呼び出すことができます。

継承の仕組みをみて、いくつか疑問がわくと思います。
そのうちの何個かを試してみたいと思います。
■同じ名前のメソッドが定義されていた場合どうなるのか

```py:Python
class Parent:
    val = "Parent"

    def all_function(self):
        print("ParentMethod:" + self.val)

class Child(Parent):
    val = "Child"

    def all_function(self):
        print("ChildMethod:" + self.val)

parent = Parent()
print(parent.val) # ⇒ Parent
parent.all_function() # ⇒ ParentMethod:Parent
child = Child()
print(child.val) # ⇒ Child
child.all_function() # ⇒ ChildMethod:Child
```

このように継承元と継承先に同じ名前の変数、メソッドが定義されている場合、作成したインスタンスのクラスの内容が優先されます。
ただし、継承先のメソッド内からなら、継承元の変数、メソッドを「super().」という修飾子を付けることで呼び出すことが可能です。

```py:Python
class Parent:
    val = "Parent"

    def all_function(self):
        print("ParentMethod:" + self.val)

class Child(Parent):
    val = "Child"

    def all_function(self):
        print("ChildMethod:" + self.val)
        super().all_function()

child = Child()
child.all_function()
# ⇒ ChildMethod:Child
# ⇒ ParentMethod:Child
```

■コンストラクタはどうなるのか

```py:Python
class Parent:
    def __init__(self):
        print("Parentコンストラクタ")

class Child(Parent):
    def __init__(self):
        print("Childコンストラクタ")

child = Child()
# ⇒ Childコンストラクタ
```

コンストラクタは作成したインスタンスのコンストラクタのみ呼び出されます。
継承元クラスのコンストラクタを呼び出したいときは、継承先のコンストラクタ内で継承元のコンストラクタを呼び出すようにします。

```py:Python
class Parent:
    def __init__(self):
        print("Parentコンストラクタ")

class Child(Parent):
    def __init__(self):
        super().__init__()
        print("Childコンストラクタ")

child = Child()
# ⇒ Parentコンストラクタ
# ⇒ Childコンストラクタ
```


##staticメソッド、classメソッド
###staticメソッド
まずは、staticメソッドから説明していきます。
staticメソッドとは、インスタンスを生成しなくてもクラスから直接呼び出すことができるメソッドです。
定義するためには、「@staticmethod」でデコレートします。
インスタンスメソッドとは異なり、引数にインスタンスを受け取りません。

```py:Python
class TestStaticMethod:
    @staticmethod
    def function():
        print("staticメソッド")

TestStaticMethod.function() # ⇒ staticメソッド
testStaticMethod = TestStaticMethod()
testStaticMethod.function() # ⇒ staticメソッド
```

このようにインスタンスからも呼び出すことが可能です。

###classメソッド
classメソッドとは、インスタンスを生成しなくてもクラスから直接呼び出すことができるメソッドです。
定義するためには、「@classmethod」でデコレートします。
インスタンスメソッドとは異なり、引数にインスタンスを受け取りませんが、かわりにクラスを受け取ります。

```py:Python
class TestClassMethod:
    @classmethod
    def function(cls):
        print("classメソッド:{}".format(cls))

TestClassMethod.function() # ⇒ classメソッド:<class '__main__.TestClassMethod'>
testClassMethod = TestClassMethod()
testClassMethod.function() # ⇒ classメソッド:<class '__main__.TestClassMethod'>
```

###staticメソッド、classメソッド使い分け
staticメソッド、classメソッド何が違うねん。と思うかもしれません。私も思いました。
違い
・デコレータが異なる
・クラスを引数に受け取るかどうかが異なる

主に2点目の違いがstaticメソッドと、classメソッドの大きな違いだと思います。
staticメソッドは、クラスを引数に受け取らないのでクラスに依存しないメソッドの定義に利用します。
classメソッドは、クラスを引数に受け取るので他のクラスメソッド等を呼び出すことが可能ですので、クラスに依存したメソッドの定義に適しています。
正直、classメソッドだけでよいのでは？と思いますがこのメソッドはクラスに依存しないよと示すためにstaticメソッドを利用しても良いかもしれません。

クラスの説明はこんな感じでしょうか。
足りないところがあれば、また足していきます。