WindowsによるPython入門 #10: クラス

# はじめに

今回はクラスを解説します。

# YouTube動画

[]()

# クラスの定義

クラスは既に解説したように「型」です。
組み込み型のみではなく、自前の型 (クラス) を定義出来ます。
以下の構文でクラスを定義出来ます。

```
class クラス名:
    変数定義
    メソッド定義    
```

具体例です。
クラスのメソッドの第一引数には、self を使用します。
変数名は何でも良いのですが、慣例的に self を使用します。

```py
>>> class Dog:  # Dog クラスを定義
...     def __init__(self, cnt):  # コンストラクタを定義
...         self.cnt = cnt
...     def bark(self):
...         print("ワンワン " * self.cnt)
...
>>> d = Dog(2)  # Dog クラスのインスタンスを作成
>>> d.bark()  # メソッドの呼び出し
ワンワン ワンワン 
>> d.cnt  # 属性を参照可能 (変更も可能)
2
>>> d  # デフォルトのクラスの表示内容
<__main__.Dog object at 0x0000029D396B3610>
```

## 継承

既存のクラスに機能を追加するには、クラスの「継承」を使用します。
また、同じメソッド名で関数を再定義すると、再定義された方が呼び出されます。
これを「メソッドオーバーライド」と言います。

派生元のクラスを「基底クラス (ベースクラス)」、派生先のクラスを「派生クラス (サブクラス)」と言います。
派生クラスから基底クラスを呼び出すには、super() を使用します。

```py
>>> class Dog:
...     def __init__(self, cnt):
...         self.cnt = cnt
...     def bark(self):
...         print("ワンワン " * self.cnt)
...
>>> class Chihauhau(Dog):
...     def __init__(self, cnt, run_cnt):  # コンストラクタをオーバーライド
...         super().__init__(cnt)  # 基底クラスのコンストラクタを呼び出し
...         self.run_cnt = run_cnt
...     def run(self):  # 新たなメソッドを追加
...         print("ラン " * self.run_cnt)
...     def bark(self):  # メソッドをオーバーライド
...         print("キャンキャン " * self.cnt)
...
>>> c = Chihauhau(2, 3)
>>> c.run()
ラン ラン ラン 
>>> c.bark()  # 派生先のメソッドが呼び出される
キャンキャン キャンキャン 
```

## クラス変数とインスタンス変数

クラスの変数には、クラス変数とインスタンス変数があります。それぞれ以下の通りです。

- インスタンス変数: インスタンスに属する変数
- クラス変数: クラスに属する変数

```py
>>> class Animal:
...     word = "ガオー "  # クラス変数
...     def __init__(self, cnt):
...         self.cnt = cnt  # インスタンス変数
...     def cry(self):
...         print(Animal.word * self.cnt)
...
>>> a = Animal(2)
>>> b = Animal(3)
>>> a.cry()
ガオー ガオー
>>> b.cry()
ガオー ガオー ガオー
>>> Animal.word = "ブーブー "  # クラス変数を書き換えると全てのインスタンスに影響が出る
>>> a.cry()
ブーブー ブーブー
>>> b.cry()
ブーブー ブーブー ブーブー
```

## インスタンスメソッドとクラスメソッドと静的メソッド

クラスで定義可能なメソッドには、インスタンスメソッドとクラスメソッドと静的メソッドの 3 通りあります。それぞれ以下の通りです。

- インスタンスメソッド: インスタンスに属するメソッド (通常のメソッド)
- クラスメソッド: クラスに属するメソッド
- 静的メソッド: インスタンスにもクラスにも属さないメソッド (関数のようなもの)

クラスメソッドと静的メソッドは、インスタンスがなくても呼び出す事が出来ます。
また、クラスメソッドと静的メソッドは、クラスを継承した時に違いが出ます。
クラスメソッドは、継承先のクラスを参照出来ますが、静的メソッドは参照出来ません。

```py
>>> class Animal:
...     word = "ガオー "
...     @classmethod
...     def cry(cls):
...         print(cls.word * 2)
...     @staticmethod
...     def cry_static():
...         print(Animal.word * 2)
...
>>> class Sheep(Animal):
...     word = "メェ "
...
>>> class Duck(Animal):
...     word = "ガーガー "
...
>>> Sheep.cry()  # Sheep クラスの word が参照される
メェ メェ
>>> Sheep.cry_static()
ガオー ガオー
>>> Duck.cry()  # Duck クラスの word が参照される
ガーガー ガーガー
>>> Duck.cry_static()
ガオー ガオー
```

## 特殊メソッド

掛け算を表す演算子の「```*```」を適用した時に、int 型のオブジェクトと　str 型のオブジェクトで挙動が異なりました。
これは、演算子の挙動を表す特別なメソッドが実装されているからです。
これを「特殊メソッド」もしくは、「マジックメソッド」と言います。
「```*```」の場合、__mul__という特殊メソッドが実装されています。
このメソッドを再定義 (オーバーロード) する事で、挙動を変更する事が出来ます。

```py
>>> class MyStr(str):
...     def __mul__(self, cnt):  # 演算子「*」をオーバーロード
...         return "[" * cnt + self.__str__() + "]" * cnt
...
>>> a ="abc"
>>> a * 3
'abcabcabc'
>>> b = MyStr("abc")
>>> b * 3  # MyStr の __mul__ が呼ばれる
'[[[abc]]]'
```

特殊メソッドは、他にも色々ありますが、ここでは省略します。

## ＿str＿と＿repr＿

例えば、bytes 型のように内部データとしては、「0 から 255 までの整数値の配列」なのに対して、print() 関数などで表示されるのは、「バイト列の文字列」です。
このように、「データ」と「表示」は異なる概念です。
クラスのオブジェクトの表示内容を実装する特殊メソッドが、```__str__``` と ```__repr__``` です。

```__str__``` と ```__repr__``` の違いは以下の通りです。
- ```__str__```: ユーザが読みやすい文字列
- ```__repr__```: eval() 関数などで元に戻せる文字列

```__str__``` 呼び出しが必要な場面で、```__str__``` が実装されていない場合は、```__repr__``` が呼び出されます。
(逆に、```__repr__``` 呼び出しが必要な場面で、```__repr__``` が実装されておらず、```__str__``` が実装されていても、```__str__``` は呼び出されず、デフォルトの文字列が取得されます)

```py
>>> class BracketStr(str):
...     def __str__(self):
...         return "[" + super().__str__() + "]"
...
>>> s = BracketStr("abc")
>>> print(s)  # __str__が呼ばれる
[abc]
>>> s.__str__()
'[abc]'
>>> s  # __repr__が呼ばれる
'abc'
>>> s.__repr__()
"'abc'"
>>> eval(_)
'abc'
```

# データクラス

クラスにメソッドは不要でデータのみ必要な場合に、Python 3.7 からデータクラスというものが用意されました。
以下の例のように使用します。

```py
>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class Person:
...     name: str
...     age: int
...     gender: str = "Male"  # デフォルト値を指定可能
...
>>> p = Person("PythonMan", 100)
>>> p
Person(name='PythonMan', age=100, gender='Male')
>>> p.name, p.age, p.gender
('PythonMan', 100, 'Male')
>>> p.__str__()
"Person(name='PythonMan', age=100, gender='Male')"
>>> p.__repr__()
"Person(name='PythonMan', age=100, gender='Male')"
```

他にも色々機能はありますので、他は公式ドキュメントをご参照下さい。

[dataclasses --- データクラス](https://docs.python.org/ja/3/library/dataclasses.html)

# おわりに

以上、クラスに関して解説しました。
次回は、モジュールに関して解説します。