Python 3.12で型ヒントがもっと便利に！新機能の紹介と活用例

## はじめに

Pythonは柔軟性と書きやすさ・読みやすさから開発者に人気なプログラミング言語の1つです。
しかし、動的型付け言語であるため、コードが大規模化してくると型に関連するバグや可読性低下などの問題が発生してきます。
こういった型起因の問題を軽減するために役立つのが型ヒント(Type Hint)です。

型ヒントはPython3.5から導入された機能で、コードに型情報を付与することでデータの型を明確するための仕組みです。
静的解析ツールやIDEの機能を使ってバグを早期に発見することができますし、型情報が付与されるため可読性も高まります。

特に近年は型ヒントが進化し、より直感的な記述ができるようになってきています。
本記事では、Python3.12で追加された型ヒントの新機能についていくつか紹介します。


## Python3.12で追加された新機能の紹介

### 1. type構文を使った型エイリアス定義

#### 型エイリアスとは

型エイリアスとは、型情報に名前をつけることです。型エイリアスをつかうことでコードの可読性と再利用性の向上が期待できます。

下記は型エイリアスの基本的な記述例です
変数に型情報を代入します。

```python
# 数値の型エイリアス
Number = int | float

# 座標の型エイリアス
Coordinates = list[tuple(float, float))
```

#### ⭐️type構文(type statement)

しかし、上記の記述では変数と型エイリアスの判別はぱっと見わかりづらいです。

そこで、Python3.12で追加されたtype構文(type statement)を使って型エイリアスの定義を明示します。

記述は単純に型エイリアス定義の前に`type`をつけるだけです。

```python
type Number = int | float
type Coordinates = list[tuple(float, float))
```

type構文により型エイリアスの定義であることがわかりやすくなりました。

https://docs.python.org/ja/3/reference/simple_stmts.html#the-type-statement

:::note warn

Python3.10で型エイリアスを明示するための`typing.TypeAlias`が追加されましたが、type文の導入により`typing.TypeAlias`は非推奨になりました。

```python
from typing import TypeAlias

Number: TypeAlias = int | float
```
:::

### 2. 型パラメータ構文を使ったジェネリクス

#### ジェネリクスとは

ジェネリクスは型に依存しない汎用的なコードを記述するための仕組みです。
特定の型に依存せず、さまざまな型を扱う柔軟なクラスや関数を作成することができます。

#### 型変数とは

型変数はジェネリクスを実現するための部品です。
型変数を利用して入力型と出力型が関連付けられた関数やクラスを作成することができます。

`typing`モジュールの`TypeVar`を使用して型変数`T`を定義します。

```python
from typing import TypeVar

T = TypeVar("T")
```

#### ジェネリクスの実装例

下記はPython3.12より前の記述例です

##### 関数の例

下記は型変数を使ったジェネリック(汎用的)な関数の例です。

`double_value`関数の引数`value`と返り値の型に型変数`T`を指定します。

```python
from typing import TypeVar

T = TypeVar("T")


def double_value(value: T) -> T:
    return value * 2 


get_value(123)  # => 246
get_value("hello")  # => "hellohello"
```

##### クラスの例

クラスの場合は`typing`モジュールの`Generic`を使用して下記のように記述します。

```python
from typing import Generic, TypeVar

T = TypeVar("T")


class A(Generic[T]):
    def __init__(self, value: T) -> None:
        self.value = value

    def get_value(self) -> T:
        return self.value
```

#### ⭐️型パラメータ構文(type parameter statement)

Python3.12で型パラメータ構文(type parameter statement)が追加されました。

以前は、上述の通り`TypeVar`を使用して型変数を定義していましたが、型パラメータ構文の導入により`TypeVar`を使用せず型変数を定義できるようになりました。

例の関数とクラスを型パラメータ構文で書き換えた記述を下記に示します。

```python
def double_value[T](value: T) -> T:
    return value * 2 
```

```python
class A[T]:
    def __init__(self, value: T) -> None:
        self.value = value

    def get_value(self) -> T:
        return self.value
```

`TypeVar`を使ったジェネリクスの記述よりもシンプルになりました。
型変数`T`をグローバル宣言する必要がなくなり、関数・クラス内で完結しているのもいいと思います。

https://docs.python.org/ja/3/reference/compound_stmts.html#type-params


### 3. メソッドのオーバーライド

#### ⭐️`@override`デコレーター

Python3.12で`typing.override`が追加されました。
継承元のメソッドを上書きしていることを明示する機能です。

メソッドのオーバーライドすることを表す機能があればなぁと思ってたところ追加されました。

使い方はオーバーライドするメソッドに`@override`デコレーターを付与するだけです。

```python
from typing import override


class Parent:
    def run(self) -> None:
        print("Base")


class Child(Parent):
    @override
    def run(self) -> None:
        print("Child")
```

単純な例なのであまり有用性を感じないかもしれませんが、規模が大きくなってくると子クラス固有のメソッドとオーバーライドしたメソッドの分別が難しく可読性の低いコードになります。
`@override`を使用することでメソッドがオーバーライドされていることが明示されて可読性が高まります。

https://docs.python.org/ja/3/library/typing.html#typing.override

## まとめ

Python3.12で追加された新機能について紹介しました。

単純なコードであれば型ヒントがなくても十分だと思いますが、コードの規模が大きくなると型起因の問題の発生が考えられます。
規模が大きく型ヒントを利用されてない場合、入力と出力が分かりづらくコードを読むのが大変だと思ったことがあります。

型ヒントを取り入れて、堅牢で保守性の高いコードを目指しましょう！

## おわりに

ここまで読んでいただきありがとうございます！
引き続きアドベントカレンダー続きますのでよければ明日以降の記事もご覧ください！

https://qiita.com/advent-calendar/2024/intimatemerger

もし弊社インティメート・マージャーに興味があればご覧ください！

https://recruit.intimatemerger.com/

https://corp.intimatemerger.com/