python で学ぶ「オブジェクト指向」

こちらは[ITRC Advent Calendar 2019](https://adventar.org/calendars/4759) 18日目の記事です。
[前回の記事](https://yukimura4423.hatenadiary.jp/entry/2019/12/18/022722)
次回 : [??]()

# 経緯
よ〜し！Rustのプログラムかくぞ〜！

ん？ここなんでエラー出てるんだろ？
ん？なおらんぞ？
ここを直せば！
直った？いや、エラーが増えた！？
やばい日付かわった。。

ん？先週と同じだって..?
知らないな〜〜〜〜なんの事かな〜〜〜〜？？？？？？


---
みなさんこんにちは! @saba383810です。rustで他のことをやろうとも思ったのですがエラーや使いたいクレートがうまくインストールできないなどの事件があったので今回はゆるしてちょ。そこでなにを書くか考えていたところ授業でちょうどオブジェクト指向について触れていてオブジェクト指向について理解し始めたのでメモ程度に記事を書きたいと思います。
今回はpythonのサンプルコードをみながら説明していこうと思うのですが、pythonのオブジェクト指向は言語使用が緩いのもあり完璧ではないのです。でもだからこそpythonはオブジェクト指向に慣れるために向いてるのではと思います。初学者ながらがんばってまとめてみたのでどうぞ！

注意 : 間違った知識がある場合があるので予めご了承ください。


# オブジェクト指向とは
オブジェクト指向についてネットで調べると
- モノとして考えると簡単
- 車の例
- オブジェクトはモノ
- クラスは分類

などと様々な人が初学者にわかりやすく説明しようと努力しています。どれも間違っているわけではないのですがあまりピンときませんよね。そんな中私はオブジェクト指向は**考えるもの**ではなく、**自然と理解してくるもの**だと思っています。このことを踏まえて今回は堅苦しい説明は抜きにしてpythonのソースコードをみながら自然に学んで行けたらいいなと思います。

ゼロから始める「オブジェクト指向」ってね！

# クラス
今回はクラスについて詳しく見ていきましょう。
クラスは効率よくプログラムしていくためのしくみです。クラスがやることそれは

***「データ（変数てきな）」と「処理(関数てきな)」をまとめる***

それだけですかんたんにみえるかな？

```
class クラス名:
  def メソッド名(self,引数):
    self.変数 = 値
    return 式
```
クラスはこのように書いていきます。順番にみていきましょう！

##クラスの定義

```test.py

class Person:#クラスの定義（関数とおんなじ感じにインデントするよ！）
  #クラスの内容
```

クラスの定義はかんたんで`class`という修飾をつけて自分の好きな名前で宣言するだけ！

ここだけの話、クラスと関数の区別を作るために


>クラスの最初の文字 :大文字  例） class Person:
>関数の最初の文字  :小文字  例) def person:


というプログラムをわかりやすくする命名条件があったりします。



##メソッドと属性



```python

class Person:#クラスの定義

  def getName(self):#ここがめそっどだよ！
    return  self.name #ここがデータの属性だよ！

  def getAge(self):#ここもめそっど！
    return self.age
```

メソッドとはクラス経由でしか呼び出せない関数みたいなものって考えるとわかりやすいと思う。ここに、あとで呼び出したい処理を記述しておきましょう。

データの属性ってあるけどわかりづらいよね。実際は変数みたいにつかうのでそういう感覚で多分大丈夫！


## インスタンス化

~~クラスは宣言しただけでは使用できません。~~

訂正 : ***宣言しただけでも関数として使用でき、***インスタンス化することで、個別の処理（メソッド）やデータを持てるようになるようです。

そこでクラスを外で使用できるようにする作業が必要です。それが***インスタンス化***なのです。

```python
person = Person() #インスタンス化
```

インスタンス化された`person`は***インスタンス***となります。

`person`にはまだなんの値も入っていません。
そこで

```python

person.name = "さば"
person.age = 19
```

このように入力することで代入することができます。

代入した値を出すためには先ほど作成したメソッドを使います。

```python

person = Person() #インスタンス化
perrson.name = "さば" #データの属性を付与
person.age = 19 #データの属性を付与
playerName = person.getName() #Person()で定義したメソッドを使用
PlayerAge  = person.getAge() #Person()で定義したメソッドを使用
```
インスタンス化したことで`person ≒ Person()`なので`person.getName()`でPersonの中のメソッドにアクセスすることができます。


## コンストラクタ

先ほどはデータの属性を後から付与していたと思います。しかしそれではオブジェクト指向の良さをあまり引き出せていません。ここではインスタンス化するときにデータの属性を付与する***コンストラクタ***について説明します。

コンストラクタとはインスタンス化されたときに~~一度だけ実行される処理~~のことです。簡単に言えば***初期化***です。

訂正 : `__init__`メソッドは初期化メソッドで間違いがないのですがコンストラクタにはそれ以外にも`__new__`メソッドというものあるので「コンストラクタ=`__init__`メソッド」は間違っているそうです。後から呼び出すことも一応は可能とのことです。、

```python

class Person:
  def __init__(self,name,age):#　コレがコンストラクタ
    self.name = name
    self.age = age

person = Person("さば",19) #インスタンス化するときに引数がある。
```

`__init__`と記述された文はインスタンス化されたときに一度だけ引数を持って実行できます。
この時点では「後から代入してもよくね？」と感じると思いますがこの後説明するカプセル化と使用すると効果を引き出せるので今はふーんと聞き流してください。

## カプセル化 

pythonは言語仕様が緩いと最初に話したのですが、そのせいで後からクラスの属性にアクセスしマイナスの値や異なる値を代入できてしまいます。

pythonのクラスを定義する際には、

>アクセスを制限したい属性の名前を
>アンダースコア1つ(_)から始まる名前とする

という方法が、慣習として行われているそうです。

```python

class Person():
  def__init__(self,name,age):
    self._name = name # <-アンダースコアが1個ついている
    ...
```

それでも間違えて***アンダースコア(_)***を書いてしまい書き換えしてしまうということもあると思います。pythonでは強制的に書き換えができないようにすることができます。そのためには

>属性の名前をアンダースコア2つ(__)から始まる名前にする

という方法で記述します

```python

class Person():
  def__init__(self,name,age):
    self.__name = name # <-アンダースコアが2個ついている
    ...
```
このように外からクラス内の属性がカプセルに入っているかのように中が見えないようにすること（隠蔽すること）を***カプセル化***といいます。

追記 : 隠蔽することだけがカプセル化ではなく、複雑な処理やプロトコルを隠して使いやすくするのもカプセル化の役割らしいです。
さらに一般的には、アンダースコアを二つ付けるやり方は、サブクラスと名前が衝突した時のみ使用した方が良いらしいです。あまり推奨されないやり方なんですね。。
#まとめ

疲れたので今回はここまでにします。

今回の内容を簡単にまとめると

- クラスの定義は簡単
- クラスの中には属性(変数のようなもの)とメソッド(関数のようなもの)がある
- インスタンス化はクラス外でしようできるようにする。
- コンストラクタとカプセル化で安全なプログラムを書く

です。
簡単に書くために言葉を選んで書いてみたのですがどうでしたか？
間違っているところがあったら遠慮せずにコメントしていただけると嬉しいです。

追記 : 間違っている知識があり多々訂正させていただきました。

#参考資料
- [やさしいPython](https://www.sbcr.jp/product/4797396027/) 著者: 高橋麻奈
- https://eng-entrance.com/what-oop