【Python】mypyでデータ型を確認したい

### はじめに
<バージョン>
python: 3.6.8
mypy: 0.901

　mypyは__変数のデータ型を確認してくれるライブラリ__です。pythonはデータ型を
宣言しなくても動作するのですが、結局は頭の中でデータ型を意識しながら書いているので、
明示的に宣言しておいた方が安全なのではないかと思います。

### 1. インストール方法
　mypyは標準ライブラリではないのでインストールしましょう。

```text
pip install mypy
```

### 2. 記載方法
　mypyを使用する場合は通常の変数の書き方と違い、変数のパターンも書く必要があります。
リスト型と辞書型はその中の要素のデータ型も記述することが可能です。

- 通常の変数の書き方

```python
# 例) var = "a"
変数名 = 変数の中身
```

- mypy使用時の変数の書き方

```python
# 例) var: str = "a"
変数名: パターン = 変数の中身
```

|データ型|パターン|記入例|
|---|---|---|
|string|str|sample_var: str = "a"|
|int|int|sample_var: int = "1|
|float|float|sample_var: float = 1.5|
|boolean|bool|sample_var: bool = True|
|list|typing.List[要素のデータ型]|sample_var: typing.List[int] = [1, 2, 3]|
|dict|typing.Dict[keyのデータ型, valueのデータ型]|sample_var: typing.Dict[str, str] = {"name": "test"}|

 ここで注意なのが、listとdictはpythonのバージョンによって書き方が異なります。

- python < 3.9の場合
  - 最初に__import typing__を記入する必要がある

```text
  import typing
  sample_list: typing.List[int] = [1, 2, 3]
```

- python >= 3.9の場合
  - __import typing__は不要

```text
  sample_list: list[int] = [1, 2, 3]
```

### 3. 実行例
#### 3-1. stringの場合
　ここでは変数の中身が文字列であるかを確認します。そのために以下の2つパターンを用意しました。

- パターン1(sample_var1)
  - 文字列を代入した場合

- パターン2(sample_var2)
  - 値を代入した場合


mypyの実行方法は以下の通りです。

```text
mypy ファイル名
```

エラーの出力を見ると、4行目で変数の宣言に誤りがあることを教えてくれます。

```python:test1.py
# success
sample_var1: str = "abc"
# fail
sample_var2: str = 123
```

```text:出力
test1.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "str")
Found 1 error in 1 file (checked 1 source file)
```

#### 3-2. listの場合

　ここではリストの中身が全て値であるかを確認します。そのために以下の3つパターンを用意しました。

- パターン1(sample_var1)
  - 要素が全て値の場合

- パターン2(sample_var2)
  - そもそも要素ではない場合(今回は値)

- パターン3(sample_var3)
  - 要素の一部が文字列である場合(今回は値)

```python:test2.py
import typing
# success
sample_var1: typing.List[int] = [1, 2, 3]
# fail - 1
sample_var2: typing.List[int] = 1
# fail - 2
sample_var3: typing.List[int] = ["a", "b", 3]
```

エラーの出力を見ると、以下のことがわかります。

- パターン2(sample_var2)
  - 変数がリストではないのでエラー
- パターン3(sample_var3)
  - 0番目と1番目の要素が値ではないのでエラー

```text:出力
test2.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "List[int]")
test2.py:7: error: List item 0 has incompatible type "str"; expected "int"
test2.py:7: error: List item 1 has incompatible type "str"; expected "int"
Found 3 errors in 1 file (checked 1 source file)
```

#### 3-3. dictの場合

　ここでは辞書のkey/valueが全て文字列であるかを確認します。そのために以下の4つパターンを用意しました。

- パターン1(sample_var1)
  - key/valueが文字列である場合

- パターン2(sample_var2)
  - そもそも要素ではない場合(今回は値)

- パターン3(sample_var3)
  - 辞書の一部でvalueが値である場合

- パターン4(sample_var4)
  - 辞書の一部でkeyが値である場合


```python:test3.py
import typing
# success
sample_var1: typing.Dict[str, str] = {"name": "test"}
# fail - 1
sample_var2: typing.Dict[str, str] = 1
# fail - 2
sample_var3: typing.Dict[str, str] = {"name": "test", "number": 1}
# fail - 3
sample_var4: typing.Dict[str, str] = {1: "number", "name": "test"}
```

エラーの出力を見ると、以下のことがわかります。

- パターン2(sample_var2)
  - 変数が辞書ではないのでエラー
- パターン3(sample_var3)
  - 1番目のエントリーが、{"str": "str"}ではなく{"str": "int"}になっているのでエラー
- パターン4(sample_var3)
  - 0番目のエントリーが、{"str": "str"}ではなく{"int": "str"}になっているのでエラー

```text:出力
test3.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "Dict[str, str]")
test3.py:7: error: Dict entry 1 has incompatible type "str": "int"; expected "str": "str"
test3.py:9: error: Dict entry 0 has incompatible type "int": "str"; expected "str": "str"
Found 3 errors in 1 file (checked 1 source file)
```

### 参考記事
[Pythonで型を極める【Python 3.9対応】](https://qiita.com/papi_tokei/items/bf652696d6b98f23565a)