【Python】selfを分かりやすく説明する

# selfとは？そして、インスタンス変数とは？

```sample.py
def __init__(self, word):
    self.word = word
```

selfは、**インスタンス自身**を指します

```sample.py
self.word = word # self.インスタンス変数 = 値
```

そして、上記のコードでは**インスタンス自身(self)の```word```という変数へ、引数として与えられた```word```という変数を代入**しています。

## インスタンス変数について

インスタンス変数の特徴としては、

|  | 特徴 |
|---|---|
| 1 | 生成されたインスタンスごとに別の値を持つ変数 |
| 2 | クラス内の全メソッドで使用できる |
| 3 | クラスの外からインスタンス変数へアクセスしたい場合、インスタンスを作成しなければならない |

以上のものがあります。詳しく記載します。

以下のコードは、```Dog```クラスから、犬種のインスタンスを作成してます

```sample.py
class Dog:
    def __init__(self, name):
        self.name = name # self.インスタンス変数 = 引数のname（ここでは"チワワ"）

    def bark(self):
        return "吠えたのは、" + self.name # self.name：インスタンス変数へアクセス

chihuahua = Dog("チワワ")
print(chihuahua.name)
# > チワワ
print(chihuahua.bark())
# > 吠えたのは、チワワ
```

### 1. 生成されたインスタンスごとに別の値を持つ変数

```sample.py
chihuahua = Dog("チワワ")
print(chihuahua.name)
# > チワワ
dachshund = Dog("ダックスフンド")
print(dachshund.name)
# > ダックスフンド
```
引数によって、インスタンスが```ダックスフンド```になったりします。

### 2. クラス内の全メソッドで使用できる

```sample.py
def bark(self):
    return "吠えたのは、" + self.name
def bite(self):
    return "噛み付いたのは、" + self.name
```

例では```barkメソッド```しか記載がないですが、```bite(噛み付く)メソッド```とかを作ってもそこで```self.name```が使用できます

### 3. クラスの外からインスタンス変数へアクセスしたい場合、インスタンスを作成しなければならない

```sample.py
chihuahua = Dog("チワワ")
chihuahua.name
# インスタンス名.インスタンス変数
```

インスタンスを生成してアクセスしています。

https://uxmilk.jp/41600

# そもそもな何故に、まどろっこしいselfを書くのか？

以下の記事でわかりやすく書かれているので、理解することが出来ます

https://codor.co.jp/python/reason-to-use-self

簡単にまとめてみます。

## selfを書く意味

結論から言うと、
**オブジェクトからメソッドへアクセスした時に、引数であるオブジェクトが省略されているため、selfを記載しています**

どういうことかというと、
実は、**[組み込み関数](https://docs.python.org/ja/3/library/functions.html)にヒント**があります。

### 組み込み関数

**プログラミング言語ごとにあらかじめ用意されている関数**の事です。
例えば、最初の文字列を大文字で返す```capitalize()```であれば

```sample.py
print(str.capitalize("python"))
# > Python
```

引数に```"python"```という```オブジェクト```をとっています

続いてこちら、

```sample.py
language = "python"
print(language.capitalize())
# > Python
```

なんと、引数を取っていないにも関わらず期待通りの出力がされています。

**なぜか？**

実は、**暗黙的に```language.capitalize(language)```が実行**されています

こちらが```capitalize()```が定義された場所

```buittins.pyi
class str(Sequence[str], _str_base):
    # 省略
　　　　　　　　def capitalize(self) -> str: ...
```

引数には、selfをとっています。

そして、
```language.capitalize(language)```が見ずらいということで、引数となるオブジェクトを省略した```language.capitalize()```でOKってことになっています

# selfを書かなかった場合について考えてみる

## 正：コンストラクタ

```sample.py
class SampleClass():
    def __init__(self, word):
        self.word = word

sample = SampleClass("Hello World")
print(sample.word)

# > Hello World
```

`__init__`というコンストラクタが最初に実行されます
クラスの外で、```sample```というインスタンスを作成して、インスタンス変数```word```へアクセスしています

`Hello World`が正しく出力されています

## 誤：selfを抜いてみる

```sample.py
class SampleClass():
    def __init__(word):
        word = word

sample = SampleClass("Hello World")
print(sample.word)

# > TypeError: __init__() takes 1 positional argument but 2 were given
```

`self`をそのまま取り外したコードになります
すると案の定エラーですね。
`TypeError: __init__() takes 1 positional argument but 2 were given`

以下の例でも確認してみましょう

## 正：クラスメソッド

```sample.py
class SampleClass():
    def call(language):
        print(language)

SampleClass.call("python")

# > python
```

こちらのコードは```SampleClass```というクラスから、クラスメソッドにアクセスしています
直接だと、```self```がなくても問題なく出力できました

一方で、、、

## 誤:インスタンスを作成してアクセス

```sample.py
class SampleClass():
    def call(language):
        print(language)

language = SampleClass() # インスタンス作成
language.call("python")

# > TypeError: call() takes 1 positional argument but 2 were given
```

インスタンスを作成して、そこから```SampleClass()```クラス内のメソッドを呼び出してみると、

```TypeError: call() takes 1 positional argument but 2 were given```
**引数は1つ欲しいのに、2つ与えられていると**エラーが

これがつまり、インスタンスからアクセスしたため、
**隠れた```self```が与えられている**ということですね

# 参考

https://qiita.com/ishigen/items/2d8b6e6398743f2c8110

https://kanda-it-school-kensyu.com/python-basic-contents/pbc_ch04/pbc_0401/