python 入門　クラス

# そもそもクラスとは？？
一つ例を示して考えてみましょう。例えば飲み物について考えてみましょう。
まず飲み物でできることを考えてみましょう
・飲むことができる
・コップに注ぐことができる。
このようなある分野で同じようなことをできるのをまとめているのをクラスと言います。
では飲み物で例を示していきたいと思います。
## classによる定義

```py
class Drink():
    pass
```
passというのは存在はしているけどなんの操作もしませんよ。ということになっています。またクラスを定義するにあったって最初は大文字にしましょう。これでDrinkというクラスは出来上がりました。次にクラスにインスタンスをいれていきます。

```py
cider = Drink()
```
と定義することができます。これでDrinkクラスにサイダーが入った感じになります。次にサイダーの中にも商品名がたくさんありますね。それを表示できるようにしましょう。引数に商品名を入れるとそれはインスタンスと同じくDrinkクラスにいれられます。まず`__init__（）`関数を定義していきます。これはこのクラスがPythonのクラスであることを表しています。ここに色々な情報を入れることができます。

```py
class Drink():
    def __init__(self, name):
        self.name = name
```
少しずつ説明していきます。まず`__init__()`の中に入っている引数の二つについて説明していきます。まずselfはクラスの中での関数を定義する時に必須となってくる。私はこのselfをクラスの中で関数を定義するっていうのが少し謎だったためこれはインスタンスだと考えた。これはあっているかわからないので気にしなくてもいいが、こうすると少しだけわかりやすく感じる。ciderはselfであると考えたら残るはnameでそれは引数にいれるという動作だけで済む。僕はそう考えているのでってことで参考にしてみてください！そしてこれからクラスにciderの三ツ矢サイダーを追加していきましょう。

```py
cider = Drink('三ツ矢サイダー')
```
これでインスタンスの作成ができた。これからこのように定義したインスタンスの三ツ矢サイダーを表示できるようにやってみよう。

```py
print('これから私が飲むものは', cider.name)
# 実行結果
これから私が飲むものは三ツ矢サイダー
```
このようにしてciderのnameを出すことができた。この時にこう考えてみてはどうだろうか。selfはcider,nameは'三ツ矢サイダー'こう考えるとselfはciderとわかりやすく考えることができる。

##継承
これから継承について説明していく。元あるクラスをどんどん編集したりするのもいいが、どんどんついかするかたちでやってみたらどうであろうか。またこの場合だとDrinkの中にも炭酸や果実ジュースなどがありますよね？そういう分け方もできるのです。

```py
class Drink():
    def drink(self):
        print('This is Drink')

class Sparkling(Drink):
    def sparkring(self):
        print('This is Sparkling')

class FruitJoice(Drink):
    def fruit(self):
        print('This is Fruit joice')
joice = Drink()
cider = Sparkling()
bananajoice = FruitJoice()
cider.drink()
cider.sparkring()
bananajoice.fruit()
joice.drink()
# 実行結果
This is Drink
This is Sparkling
This is Fruit joice
This is Drink
```
ここで一ついうことがある。
継承元のクラスは親クラスといい、継承しようと思っているものを子クラスという。ここでいう親クラスはDrinkで子クラスはSparklingとFruitJoiceです。また親クラスで定義したインスタンスは子クラスの関数を使うことができないので注意しよう。

# メソッドのオーバーライド
これからは今ある親のメソッドを上書き（オーバーライド）していきましょう。

```py
class Drink():
    def drink(self):
        print('This is Drink')

class Sparkling(Drink):
    def drink(self):
        print('I want to drink')
a = Drink()
b = Sparkling()
a.drink()
b.drink()
# 実行結果
This is Drink
I want to drink
```
このようにオーバーライドすることができる。しかし上書きと言われるとDrinkにいれたaインスタンスのdrink関数も上書きされるのではないかと思うのだがどうだろうか。この続きにこのようなことを書いてチェックしてみましょう

```py
a.drink()
# 実行結果
This is Drink
```
このように親クラスで定義したインスタンスは上書きされても関数は変わらない。

# superによる親メソッドへの呼び出し
子クラスで独自に定義したり、オーバーライドをしたりするやり方は今習得した。しかし子クラスが親メソッドを呼び出したい時にどうすればいいのか。それはsuperを使う

```py
class Drink():
    def __init__(self, name):
        self.name = name

class Sparkling(Drink):
    def __init__(self, name):
        super().__init__(name)
cider = Sparkling('Mityuya')
print(cider.name)
# 実行結果
Mityuya
```
このようにDrinkの`__init__`関数をそのまま使うことができます。これをすると何がいいのかというと例えばいきなりドリンクに株式会社と名前をつくことが義務付けられたらどうなるだろうか。いちいち全て変えようとするのはめんどくさい。いざという時のためにってことのためにsuperを全てやっておけばこのような変更

```py
class Drink():
    def __init__(self, name):
        self.name = "株式会社" + name
```
と変更することができる。これで実行結果を確認しようと

```py
cider = Sparkling('Mityuya')
print(cider.name)
# 実行結果
株式会社Mityuya
```
これでできます。

# プロパティ
プロパティとはクラス内で定義されたものを変えられないように設定することである。例えば炭酸のものにこれは炭酸ではないと設定されたら大変ですし、炭酸では無くなりますよね？

```py
class Drink():
    def print_this_is_drink(self):
        print('This is drink')

class Sparkling(Drink):
    def __init__(self, this_is_sparkling=True):
        self.this_is_sparkling = this_is_sparkling

cider = Sparkling()
print(cider.this_is_sparkling)
# 実行結果
True
```
このままだとthis_is_sparklingを変更しようとしたら変更できます。

```py
class Drink():
    def print_this_is_drink(self):
        print('This is drink')

class Sparkling(Drink):
    def __init__(self, this_is_sparkling=True):
        self.this_is_sparkling = this_is_sparkling

cider = Sparkling()
self.this_is_sparkling = False
print(cider.this_is_sparkling)
# 実行結果
False
```
このように変更できちゃいます。これを変更できないようにしましょう。それは「self.this_is_sparkling = this_is_sparkling」にアンダースコアをつけていきます。

```py
self._this_is_sparkling = this_is_sparkling
```
これでできるのですが、_this_is_sparkling = Falseと設定してもまだ結果はFalseに変わってしまいます。ではどうすればいいのかというとプロパティを使った関数を作っていきます。Sparklingクラスの中にコのような関数をいれてください。

```py
@property
def it_is_sparkling(self):
    return self._this_is_sparkling 
```
そしたら全体的にこのような構造になると思います。

```py
class Drink():
    def print_this_is_drink(self):
        print('This is drink')

class Sparkling(Drink):
    def __init__(self, this_is_sparkling=True):
        self.this_is_sparkling = this_is_sparkling

    @property
    def it_is_sparkling(self):
        return self._this_is_sparkling

cider = Sparkling()
cider.this_is_sparkling = False
print(cider.this_is_sparkling)
# 実行結果
Traceback (most recent call last):
<module>
    cider.this_is_sparkling = False
AttributeError: can't set attribute
```
このようにエラーが出てきます。よって変更はできないということでプロパティが成立します。今のように変更できなくしたことをゲッターというものです。これからセッターという変更できるものを使っていきたいと思います。やり方はプロパティのように@マークを使用します。全体図としては

```py
class Drink():
    def print_this_is_drink(self):
        print('This is drink')

class Sparkling(Drink):
    def __init__(self, this_is_sparkling=True):
        self._this_is_sparkling = this_is_sparkling

    @property
    def this_is_sparkling(self):
        return self._this_is_sparkling
# 追加部分
    @this_is_sparkling.setter
    def this_is_sparkling(self, is_sparkling):
        self._this_is_sparkling = is_sparkling
# 追加終わり
cider = Sparkling()
cider.this_is_sparkling = False
print(cider.this_is_sparkling)
# 実行結果
False
```
is_sparklingのなかに引数としてFalseが入っている感じになっている。次にやりたいのはまずそのデータがないみたいなことをやってみたいと思います。例えば、先ほどやったthis_is_sparklingについてやっていきたいと思います。データをクラスでの定義内での変更やりようが可能だが、クラス定義内でthis_is_sparklingがどのようなものかチェックしようとするとそのオブジェクトはありませんよ。と表示されます。やってみましょう

```py
class Drink():
    def print_this_is_drink(self):
        print('This is drink')

class Sparkling(Drink):
    def __init__(self, this_is_sparkling=True):
        self.__this_is_sparkling = this_is_sparkling

    @property
    def this_is_sparkling(self):
        return self._this_is_sparkling
    @this_is_sparkling.setter
    def this_is_sparkling(self, is_sparkling):
        self._this_is_sparkling = is_sparkling
cider = Sparkling()
print(cider.__this_is_sparkling)
```
やり方は`this_is_sparkling`を`__this_is_sparkling`にするだけで可能になります。これで実行してみると

```py
Traceback (most recent call last):
  File 
<module>
    print(cider.__this_is_sparkling)
AttributeError: 'Sparkling' object has no attribute '__this_is_sparkling'
```
このようにありません。とエラーが出てきます。これをクラス定義内で`__this_is_sparkling`をチェックしてみるとどうなるかというと

```py
class Drink():
    def print_thi_is_drink(self):
        print('This is drink')

class Sparkling(Drink):
    def __init__(self, this_is_sparkling=True):
        self.__this_is_sparkling = this_is_sparkling

    @property
    def this_is_sparkling(self):
        return self._this_is_sparkling
    @this_is_sparkling.setter
    def this_is_sparkling(self, is_sparkling):
        self._this_is_sparkling = is_sparkling
    def check_sparkling(self):
        print(self.__this_is_sparkling)
cider = Sparkling()
cider.check_sparkling()
```
全体図としてはこのようになる。そうすると実行結果は

```py
True
```
とクラス定義内だとチェックできる。

# クラスメソッド
クラスメソッドはクラス全体に影響を与えるメソッドでクラスに加えた変更は全てのクラス内でのオブジェクトに影響を与える。クラスメソッドの定義方法としてはデコレーターを使って@classmethodを使ってやっていく。

```py
class Drink():
    count = 0
    def __init__(self):
        Drink.count += 1
    @classmethod
    def count_drink(cls):
        print('He has', cls.count)
cider = Drink()
apple_joice = Drink()
grape_joice = Drink()
Drink.count_drink()
# 実行結果
He has 3
```
これをすると現状のクラス内に入っているものや状況がわかるので楽である。
次に静的メソッドを紹介していこう。これもクラスメソッドと同様デコレーターをつけてやっていく。@staticmethodを使ってやっていくのだ。

```py
class Drink():
    @staticmethod
    def drink():
        print('This is drink')
Drink.drink()
# 実行結果
This is drink
```
このようにオブジェクトを作らないでもメソッドを実行することができる。

# ダックタイピング
ダックタイピングとはクラスが別々のものを同じメソッドを使用することができてそれぞれのクラスで作ったものを同じ操作でそれぞれの処理をできることをさします。要はクラスが違くても同じ関数で場合分けすることができるよってことです。試しに投票というものでやってみましょう。大人でない方は投票することができず、大人の方は投票することができる。そんなものを作ってみましょう。

```py
class Person():
    def __init__(self, age=0):
        self.age = age
    def vote(self):
        if self.age < 18:
            print('You cannot vote')
        else:
            print('You can vote')

class NotAdult(Person):
    def __init__(self, age=0):
        if age < 18:
            super().__init__(age)
        else:
            print('You can vote')

class Adult(Person):
    def __init__(self, age=18):
        if age >= 18:
            super().__init__(age)
        else:
            print('You cannot vote')
class Vote(object):
    def vote_yes(self, which_adult):
        which_adult.vote()

not_adult = NotAdult()
adult = Adult()
votes = Vote()
votes.vote_yes(not_adult)
votes.vote_yes(adult)
# 実行結果
You cannot vote
You can vote
```
> 細かく説明していきましょう。
1Personという親クラスを作成し、子クラスが使える場合分けをする（１８歳かどうか）関数を作ります。
2１８歳以上なのかどうなのかという子クラスをそれぞれ作っていきます。ここではデフォルト引数が設定されていてそれぞれのelse文にはそうでない場合はErrorが出るようにします。（ここでは、ダックタイピングなのでそのエラーは表示されない。）
3最後にそれらをまとめるものを作ります。そしてそれぞれの子クラスが入った時に先ほど１で作成した関数が使えるようにしていきます。これで完了です。

# 特殊メソッド
今まで学んできた特殊メソッドとして`__init__`があります。この他にもたくさんあるので紹介していきます。まず特殊メソッドがある意味について教えていきたいと思います。特殊メソッドの代表的なものとして`__eq__`というものがあります。これは一方と他方が等しいことを表します。これを使ってイコールを示すのか、特殊メソッドを使って示すのどちらが楽かということをやっていきたいと思います。

```py
class A():
    def __init__(self, a):
        self.a = a

    def equal(self, b):
        return self.a == b.a
    def __eq__(self, b):
        return self.a == b.a

first = A('a')
second = A('a')
# equal関数を使った時
print(first.equal(second))
# 特殊メソッドを使った時
print(first == second)
```
結果はどっちともTrueになりますが、どちらがみやすくて描きやすいですかと言われますとやっぱり`__eq__`の方だと思います。このようにみやすく書きやすくされたものが特殊メソッドだと思います。

他にもたくさんの特殊メソッドがあるのでチェックしてみてください。
https://blog.codecamp.jp/python-class-code