Pythonで厳密な型付けをするということはどういう事なのか

## はじめに
おはようございます、しなもんです。

Pythonは、その簡潔さと柔軟性を生かすため
基本的に**動的型付け言語**として設計されています。

動的型付けとは、**変数の型が実行時に決定**され、**必要に応じて変更**できることを意味します。
そのため、割と無茶なコードが通ったりすることがあります。

```python
x = 5  # xは整数型
x = "Hello"  # xは文字列型に変更可能
```

**めちゃくちゃです。**

一方、静的型付けは変数の型を事前に宣言し、コンパイル時にチェックする方式です。
これにより、型関連のエラーを早期に発見できます。

単純にエラーが発生する可能性がめちゃくちゃ増えるので面倒くさそうですが、
しっかり動作するプロダクトを作る上では非常に大切な考え方であるわけです。

そこでPythonは、バージョン3.5から型ヒント（Type Hints）を導入しました。
これにより、**動的型付け言語でありながら、静的型チェックの恩恵を受けられる**ようになりました。

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

## なんでそんな厳密に型付けしなきゃいけないのか

厳密な型付けは先述した通り、バグを減らすために開発において重要な役割を果たします。

- **型による安全が保証できる**
型の不一致によるバグを事前に防ぐことができます。
intとfloatの違いによるエラーとかはよくありますよね。それを事前に防げます。

- **コードの可読性が上がる**
関数の入力と出力の型が明確になり、コードの意図が理解しやすくなります。
共同作業するときに特に威力を発揮します。

- **開発効率が爆上がりする**
IDEによる補完機能が強化され、開発スピードが向上します。
VScodeだと`Ctrl+space`で出るアレです。
個人的には一番これがありがたいです。

- **リファクタリングが簡単になる**
型情報があることで、大規模な変更を安全に行えます。

例えば、以下のような関数があるとします。
関数名からは引数が`int`なのか`float`なのか、はたまた別の型なのか分かりませんが
このように型ヒントを付けることでそこを明確にできます。

```python
def calculate_area(length: float, width: float) -> float:
    return length * width
```

## 他の静的型付け言語と比べてPythonはどういう扱いなのか

静的型付け言語としてTypeScriptを挙げます。

- **型付けは任意**
Pythonの型ヒントはあくまで任意で、型ヒントがなくてもコードは動作します。

   ```python
   def add(a, b):  # 型ヒントなし
       return a + b

   def add_typed(a: int, b: int) -> int:  # 型ヒントあり
       return a + b
   ```

~~TypeScriptで言うany地獄になりかねないという意味でもありますが~~
初心者が全てを完全に理解しなくても書くこともできるという点ではあります。

- **実行時に型チェックをしない**
Pythonは実行時に型チェックを行いません。型ヒントは静的解析ツールのためのものです。

- ~~適当~~ **柔軟性がある**
Pythonは依然として動的型付け言語であり、必要に応じて型を変更できます。

TypeScriptと比較すると
Pythonは、あくまで型チェックを別のステップ（静的解析ツールの使用）で行うわけです。

Pythonの型システムは、動的型付けの柔軟性を維持しつつ、
静的型チェックの利点を取り入れるハイブリッドでいい感じな言語になっているわけです。

## Pythonにおける厳密な型付けの実現

### **型ヒントの使用**

```python
def get_full_name(first_name: str, last_name: str) -> str:
    return f"{first_name} {last_name}"
```

   これに関してはどのプロジェクトに関わらず
   関数の引数や戻り値を示す型ヒントはできるだけ付けるようにしましょう。
   めちゃくちゃ便利です。

### **静的型チェッカーの活用**

```
$ mypy your_script.py
```

   mypyというのは、型の整合性を実行前に静的状態でチェックしてくれるツールです。便利。

### **高度な型ヒント技法**

#### ジェネリクス
```python
from typing import List, TypeVar
     
T = TypeVar('T')
def first(l: List[T]) -> T:
    return l[0]
```

https://qiita.com/jrfk/items/fa9e4a5922a7d3a5615b

#### Union型とOptional型
```python
from typing import Union, Optional

def process_input(value: Union[int, str]) -> Optional[str]:
    if isinstance(value, int):
        return str(value)
    elif isinstance(value, str):
        return value
    else:
        return None
```
`Union`はそのリストの中にある型のいずれかを使えることを示すものです。
この例では`int`か`str`どちらでも良いというわけです。

`Optional`というその名前から分かるように、
`None`を返す場合もあるという表現ができます。

簡単に使えるので覚えておくといいと思います。

#### Literal型:
```python
from typing import Literal

def move(direction: Literal['left', 'right', 'up', 'down']) -> None:
    print(f"Moving {direction}")
```

イミュータブルな値に対して、具体的な値しか受け付けないようにする型です。

https://qiita.com/simonritchie/items/04832ab86c962b3e51cd

## メリットと注意点

ぶっちゃけ個人的には**IDEのサポート強化**が最も大きい利点だと思います。
補完が楽になるだけでなく、**Copilotとかも型をヒントに候補を提示**してくれるようになるため
単純にコーディングが爆速になる気がします。

また、コードを中身まで読まなくても、入口と出口を把握できるようになるというだけで効果があります。
数日前に自分が書いたコードが分からないなんてことありませんか？
アレをどうにかできます。

また、潜在的なバグを発見することもできるようになります。

注意点としては、プロジェクトによりますが
Pythonの大きな特徴である柔軟性が低下することです。
~~個人的には柔軟性が低下して困ったことはありませんが~~

また、単純に初心者に対しては学習コストが増加するという点もあります。

## ベストプラクティスと実践例

- **適切な粒度での型指定**
すべての変数に型ヒントを付ける必要はないので、重要な部分に集中しましょう。

- **gradual typingの活用**
プロジェクトの一部から徐々に型付けを導入していくアプローチを取ります。


***
最終的にこんなコードが書かれていたら個人的にはうれしいです。

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

   class User:
       def __init__(self, name: str, age: int):
           self.name = name
           self.age = age

   def get_user_by_id(user_id: int, users: List[User]) -> Optional[User]:
       for user in users:
           if user.id == user_id:
               return user
       return None

   def group_users_by_age(users: List[User]) -> Dict[int, List[User]]:
       groups: Dict[int, List[User]] = {}
       for user in users:
           if user.age not in groups:
               groups[user.age] = []
           groups[user.age].append(user)
       return groups
   ```

## さいごに

Pythonで行う厳密な型付けは、動的型付けの柔軟性と静的型チェックの安全性を両立させることができる特別な仕組みです。
Pythonを学んでいる人には是非実践してみてほしい機能です。

- [Python公式ドキュメント: Type Hints](https://docs.python.org/3/library/typing.html)
- [PEP 484 – Type Hints](https://www.python.org/dev/peps/pep-0484/)