型ヒントのお試し


# 概要

[PEP 484: Type Hints](https://www.python.org/dev/peps/pep-0484/) がPython3.5で導入されました。詳しくは、[公式ドキュメント 型ヒントのサポート](https://docs.python.org/ja/3/library/typing.html) を参照してください。

最近、コミュニティ内でも導入している話を多く聞くようになりました。私も今後のプロジェクトで導入を進めたいと思い、自身の勉強と他の人に説明するときの参考資料にするためにここにまとめます。


型ヒントは、Pythonの文法としてPython3.5以降で導入されました。

- ヒントなので、実行時には何も影響を与えない
- 組み込みの公式ツールはない



# ツール

- mypyというデファクトのチェックツールがある(pipでインストール可能)
- PyCharmなどのエディタが対応し、コード補間が強力になり、チェックもしてくれる
- ヒントを記載した場所(モジュール単位でも関数単位でも)のみで有効になる
- ヒント書けば書くほどコード補間が効き、チェックもしてくれる

[mypyのチートシート](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html)

mypyのインストール

```
(env) $ pip install mypy
```

mypyでチェック

```
(env) $ mypy モジュール名
```


# 型ヒント定義例

例として、すべて整数(int)型で示します。

## 変数定義

```python

a: int = 10
```

```python
a: int
a = 10
```


## 関数の引数定義

```python
def f(a: int):
    pass
```


```python

def f(a: int = 10):
    pass
```

## 関数の戻り値定義

```python

def f(a) -> int:
    return 10
```

```python

def f(a: int, b: int = 10) -> int:
    return a + b
```



# 型定義

## データ型で定義

|名称|型|
|---|---|
|整数|int|
|浮動小数点数|float|
|文字列|str|
|バイト型|bytes|
|真理値|bool|
|None|None|


## コンテナ系

コンテナ系は、内包する型を指定するので、 `list[int]` などと表記を行いたい。しかし、これはPythonの文法エラーとなってしまう。その為に、typingでは専用の型を準備している

```python

from typing import List, Tuple, Dict, Set
```

|名称|型|定義例|
|---|---|---|
|リスト|List|List[int]|
|タプル|Tuple|Tuple[int, int] / Tuple[int, ...]|
|辞書|Dict|Dict[str, int]|
|セット|Set|Set[int]|


## 特殊な型


```python
from typing import Any
```

|名称|型|定義例|
|---|---|---|
|なんでもいい|Any|Any|


```python

from typing import Callable, Iterator, MutableMapping
```

|名称|型|定義例|
|---|---|---|
|呼び出し可能|Callable|Callable[[int, float], bool]|
|繰り返し可能|Iterator|Iterator[int]|
|辞書のような挙動|MutableMapping|MutableMapping[str, int]|



# その他

## Optional

Optionalは、指定の型 or Noneを、許容する型です。
例えば、整数とNoneのいずれかを許容する場合は、 `Optional[int]` とします。

以下の様なケースで利用します。

```python

from typing import Optional

def f1(a: int, b: int) -> Optional[int]:
    if b == 0:
        return None
    return a // b
```

便利なのは以下の様に使えるからです。

```python

def f2(c: Optional[int]) -> int:
    if c is None:
        c = 0
    return c + 100
```

上記のケースを以下の様に記載すると、mypyがエラーを通知します。

```python

def f2(c: Optional[int]) -> int:
    return c + 100
```

```
$ mypy sample.py 
sample.py:**: error: Unsupported operand types for + ("None" and "int")
sample.py:**: note: Left operand is of type "Optional[int]"
Found 1 error in 1 file (checked 1 source file)
```

つまり、 `if c is None` を行うことで、cがNoneの時を加え、それによりそれ以降の行では、変数c
は、整数型(int)であることが確定しているので、問題なく処理ができることを保証しています。


## Union

Unionは、2つのいずれかの型を示すことができます。
例えば、整数型 or 文字列型 と言った場合に、 `Union[int, str]` と指定します。

以下の様に引数を受け取るときに、整数か文字列かが分からないような場合に使い、 `isinstance` を使って処理を分けることで、上記のOptional同様に、変数の型を確定させる事ができます。

```python

from typing import Union

def f3(d: Union[int, str]) -> str:
    if not isinstance(d, str):
        d = str(d)
    return d.upper()
```

ここでは、 `if not isinstance(d, str):` として、変数dが文字列型(str)でないときに、文字列に変換する処理を入れて処理を継続しています。 `return d.upper()` で、 `d` が文字列であることが分かっているので、安全に大文字に変換する文字列型のメソッドが使えています。


## int と float

intと指定してfloatは入れられませんが、その反対のfloatと指定してintを入れる事ができます。
以下の場合、 num4だけがエラーがでます。

```python

num1: float = 1.0
num2: float = 1
num3: int = 1
num4: int = 1.0  # これはエラーとなる
```

```
$ mypy sample/sample.py 
sample.py:**: error: Incompatible types in assignment (expression has type "float", variable has type "int")
```

これは、intをfloatに自動変換することができるが、その反対のfloatをintにすると情報を失うことがあることに起因しているそうです。