【データサイエンティスト入門】Pythonの基礎♬関数とクラス

昨夜の[【データサイエンティスト入門】Pythonの基礎♬関数と無名関数ほか](https://qiita.com/MuAuan/items/fe594b794fcf75ae3abe)の続きで関数とクラスの話です。
【注意】
[「東京大学のデータサイエンティスト育成講座」](https://www.amazon.co.jp/%E6%9D%B1%E4%BA%AC%E5%A4%A7%E5%AD%A6%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%82%B9%E3%83%88%E8%82%B2%E6%88%90%E8%AC%9B%E5%BA%A7-Python%E3%81%A7%E6%89%8B%E3%82%92%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E5%AD%A6%E3%81%B6%E3%83%87%E2%80%95%E3%82%BF%E5%88%86%E6%9E%90-%E5%A1%9A%E6%9C%AC%E9%82%A6%E5%B0%8A/dp/4839965250/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=)を読んで、ちょっと疑問を持ったところや有用だと感じた部分をまとめて行こうと思う。
したがって、あらすじはまんまになると思うが内容は本書とは関係ないと思って読んでいただきたい。
###前振り
この章では、pythonの関数呼び出しの仕組みと，クラスの記載法と関数との違いを中心にまとめます。
関数の呼び出しの仕組みについては、以下を参考にしています。
【参考】
[モジュールの関数呼び出し@PyQ](https://docs.pyq.jp/python/library/function.html#id8)

###Chapter1-2 Pythonの基礎
####1-2-6　クラスとインスタンス
クラスからインスタンスを生成する。
ここでは、本書を少し離れて、関数呼び出しとクラスの宣言、そしてインスタンス実行の類似性を示すこととする。
###関数呼び出し
関数は、一つのPG内で利用することもあるが、複雑なPGを書こうとすると、構造的なディレクトリ構成にして、Package的に関数を保存して利用する。その場合、重要な仕組みがpythonにはある。
すなわち、以下のコードを`>python calc_add.py`で実行すると以下の順番に動作する。
①二つの関数定義が行われる
②`__name__`に`calc_add`というモデュール名が代入される
③`print`文が実行される
④`if` 文がTrueであり、以下の二つの`print`文が実行される

```calc_add.py
def calc_add(a, b):
    return a + b
    
def calc_multi(a, b):
    return a * b
       
print('__name__:', __name__)

if __name__ == '__main__':        
    print(calc_add(5,3))
    print(calc_multi(5, 3))
```
結果

```
__name__: __main__
8
15
```
####他のコード`main.py`から実行する
この場合、`>python main.py`と実行すると
①最初の宣言文により、`calc_add.py`から二つの関数`calc_add, calc_multi`を読み込む。このとき、出力を見ると分かるように上記の`calc_add.py`が実行され、`print`文が実行され`__name__: calc_add`が出力される。`if`文はFalse(`__name__ = calc_add`)なので、実行されない
②次に、main.pyのprint文が実行される
③`main.py`の`if`文はTrueなので、以下の二つの`print`文が実行される
という仕組みで、関数呼び出しして使えるようになっている。
④というわけで、`if __name__ == '__main__':` という記法が入れる必要があるというわけです。しかし、呼び出して使う場合は必須ではない。

```main.py
from calc_add import calc_add, calc_multi

print('__name__2:', __name__)

if __name__ == '__main__':   
    print(calc_add(5,3))
    print(calc_multi(5, 3))
```
結果

```
__name__: calc_add
__name__2: __main__
8
15
```
**注意）`print('__name__:', __name__)`は仕組み説明のために入れているものであり、通常は必要ない**
###class
classも見た目、関数と同じ。
ただし、classの宣言をする。
そして、クラスはif文の中を見ると分かるように、instanceを定義してから、class内で定義した関数を呼び出して利用する。
この仕組みをオブジェクト指向と呼んでいる。
すなわち、classから多くのinstanceを生成して、それを実行させる仕組みである。
クラス定義の特徴は以下の通り。
①class宣言する
②コンストラクタ`def __init__(self,x,y):`を定義する。selfという変数は必須で定義する（selfという変数は特別だけど、これを単なる変数の一つなんだと思えるようになるまで少し時間がかかった）。引数xやyはその時々で必要なものを定義する。
【参考】
[Pythonのselfとは？使い方や注意点について解説](https://www.sejuku.net/blog/64106)

```
selfについて
・selfはinstance宣言時、引数として記述しない
・selfはクラス変数である
・クラス継承の際も使える
・クラス変数にもインスタンス変数にも値がある場合は、インスタンス変数を優先して参照
```
③コンストラクタ`def __init__(self,x,y):`はクラスのinstanceを定義した時点で、最初に動くコードである。
④その他の必要な関数を定義する。
⑤`instance.calc_add()`のように必要な処理を実行する
⑥`if __name__ == '__main__':`は、関数の時と同じ理由で書くが、呼び出して使う場合は必須ではないので、多くのクラスはこのif文や実行文が無い。
**注意）`print('__name__:', __name__)`は仕組み説明のために入れているものであり、通常は必要ない**

```calc_class.py
class MyCalcClass:
    def __init__(self,x,y):
        self.x = x
        self.y = y
        
    def calc_add1(self, a, b):
        return a + b

    def calc_add2(self):
        return self.x + self.y
  
    def calc_multi(self, a, b):
        return a * b
        
print('__name__:', __name__)
if __name__ == '__main__':        
    instance_1 = MyCalcClass(1,2)
    instance_2 = MyCalcClass(5,10)
    print(instance_1.calc_add1(5,3))
    print(instance_1.calc_add2())
    print(instance_1.calc_multi(5, 3))
    instance_1.calc_print(5)
```
`>python calc_class.py`
結果
以下の通りだが、結果はinstance1の初期値に依存して計算しているのが分かる。
if文部分を削除すると`__name__: __main__`のみ出力される。

```
__name__: __main__
8
3
15
data:5: yの値2
```
####他のコード`main.py`から実行する
```main_class.py
from calc_class import MyCalcClass

print('__name__2:', __name__)

if __name__ == '__main__':
    instance_1 = MyCalcClass(1,2)
    instance_2 = MyCalcClass(5,10)
    print(instance_1.calc_add1(5,3))
    print(instance_1.calc_add2())
    print(instance_1.calc_multi(5, 3))
    instance_1.calc_print(5)
```
結果
`>python main_class.py`
もちろん、結果は↑と同じです。ただし、関数と同じように`__name__: calc_class`が出力されている。
すなわち、`__name__`には、呼び出したファイル名が入力され、自分自身のときは、`__main__`が入力される。

```
__name__: calc_class
__name__2: __main__
8
3
15
data:5: yの値2
```
###まとめ
・関数呼び出しとクラスを比較してみた
・クラス定義して呼び出して、インスタンスを実行した

・一般的なpythonコードを記述する一歩手前まで来た
・本書は、この項でpythonの基礎は終わるが、不足した部分は、今後の項で補足される（ある意味、本書はこれからもpythonの各種Libを利用してのコード解説である）