Pythonの型ヒントと静的型チェッカー（mypy）の活用方法

## はじめに

こんにちは！今回は、Pythonの型ヒントと静的型チェッカーである`mypy`の活用方法について詳しく解説します。型ヒントを使用することで、コードの可読性が向上し、バグの早期発見にも役立ちます。さらに、`mypy`を使用することで、静的型チェックを行い、型関連のエラーを事前に検出できます。

## 1. Pythonの型ヒント

Python 3.5以降では、型ヒントを使用してコードに型情報を追加できます。

### 1.1 基本的な型ヒント

```python
def greet(name: str) -> str:
    return f"Hello, {name}!"

age: int = 30
is_adult: bool = age >= 18
```

### 1.2 複合型

```python
from typing import List, Dict, Tuple, Optional

def process_data(data: List[int]) -> Dict[str, int]:
    result: Dict[str, int] = {}
    for item in data:
        result[str(item)] = item * 2
    return result

def get_user_info() -> Tuple[str, int, Optional[str]]:
    return ("Alice", 30, None)

# 関数を呼び出すコード
def main():
    # process_data関数の呼び出し
    data: List[int] = [1, 2, 3, 4, 5]
    processed_data: Dict[str, int] = process_data(data)
    print("Processed Data:", processed_data)

    # get_user_info関数の呼び出し
    user_info: Tuple[str, int, Optional[str]] = get_user_info()
    print("User Info:", user_info)

if __name__ == "__main__":
    main()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/224df006-8e0d-7aa6-5872-061387680006.png)

### 1.3 ジェネリック型

```python
from typing import TypeVar, Generic

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self) -> None:
        self.items: List[T] = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        return self.items.pop()

int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
```

## 2. mypyの導入と基本的な使用方法

`mypy`は、Pythonの静的型チェッカーです。型ヒントを利用して、コードの型エラーを検出します。

### 2.1 mypyのインストール

```bash
pip install mypy
```

### 2.2 基本的な使用方法

ファイル名が`example.py`の場合：

```bash
mypy example.py
```

### 2.3 設定ファイル

プロジェクトのルートディレクトリに`mypy.ini`ファイルを作成して、mypyの設定をカスタマイズできます：

```ini
[mypy]
strict = True
ignore_missing_imports = True
```

## 3. mypyの高度な使用方法

### 3.1 型の無視

特定の行で型チェックを無視したい場合：

```python
x = 1 + "2"  # type: ignore
```

### 3.2 動的な属性の型ヒント

```python
from typing import Any

class DynamicClass:
    def __init__(self) -> None:
        pass

    def __getattr__(self, name: str) -> Any:
        return f"Dynamic attribute: {name}"

dynamic_obj = DynamicClass()
reveal_type(dynamic_obj.random_attribute)  # Revealed type is "Any"
```

### 3.3 プロトコルを使用した構造的部分型

```python
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None:
        ...

class Circle:
    def draw(self) -> None:
        print("Drawing a circle")

class Square:
    def draw(self) -> None:
        print("Drawing a square")

def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())  # OK
render(Square())  # OK
```

## 4. mypyの活用例

### 4.1 継承と型チェック

```python
class Animal:
    def speak(self) -> str:
        return "Some sound"

class Dog(Animal):
    def speak(self) -> str:
        return "Woof!"

class Cat(Animal):
    def speak(self) -> int:  # エラー: 戻り値の型が一致しない
        return 42
```

### 4.2 条件分岐での型の絞り込み

```python
from typing import Union

def process_value(value: Union[int, str]) -> None:
    if isinstance(value, int):
        reveal_type(value)  # Revealed type is "int"
        print(value + 1)
    else:
        reveal_type(value)  # Revealed type is "str"
        print(value.upper())
```

### 4.3 ジェネリック関数

```python
from typing import TypeVar, List

T = TypeVar('T')

def first(lst: List[T]) -> T:
    if not lst:
        raise ValueError("List is empty")
    return lst[0]

result = first([1, 2, 3])
reveal_type(result)  # Revealed type is "int"
```

## 5. mypyを使用する際のベストプラクティス

1. プロジェクトの早い段階から型ヒントと`mypy`を導入する
2. CIパイプラインに`mypy`チェックを組み込む
3. `strict`モードを有効にして、より厳密な型チェックを行う
4. サードパーティライブラリの型情報（stub files）を活用する
5. `reveal_type()`関数を使用して、変数の型を確認する
6. 型ヒントは段階的に導入し、既存のコードベースに徐々に適用する

## まとめ

Pythonの型ヒントと`mypy`を活用することで、以下のメリットが得られます：

- コードの可読性と自己文書化の向上
- 早期のバグ検出
- IDEのコード補完機能の強化
- リファクタリングの際の安全性向上

型ヒントは、Pythonの動的型付けの柔軟性を損なうことなく、静的型付け言語の利点を取り入れることができる素晴らしい機能です。`mypy`と組み合わせることで、さらに強力な開発ツールとなります。

ぜひ、あなたのプロジェクトに型ヒントと`mypy`を導入して、より安全で保守性の高いPythonコードを書いてみてください！

以上、Pythonの型ヒントと静的型チェッカー（mypy）の活用方法についての記事でした。ご清読ありがとうございました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/33500267-8248-2612-17be-9ae166abcacd.png)

## 参考記事
https://qiita.com/Tadataka_Takahashi/items/53d70e8c579e88c7c314