型ヒントしたい(typing

1# 変数名: 型ヒント = 値
2year: int = 2023
3pi: float = 3.14
4holiday: tuple[int, int, int, str] = (2023, 5, 5, "こどもの日")

typingで、関数の引数などに型ヒント(=型情報)を追加できます。 Python3.5で追加された標準モジュールです。 型ヒントがあると、VS CodeなどのIDEで編集中に型チェックしてくれるようになります。 入力時に補完候補を表示してくれたり、値がマッチしていない部分をハイライトしてくれたりします。

具体的な使い方は mypyのチートシート がとても参考になります。

注釈

Python3.12+であればtypingモジュールでOK。 3.8から3.11もサポートするならばtyping_extensions(後方互換性+新機能テスト)も併用すると安全です。

変数したい

1変数名: 型ヒント = 初期値
2変数名: 型ヒント # 初期値なし

変数名: 型ヒントのように、変数名の後に: 型ヒントをつけて型ヒントを定義できます。

1x: int = 42          # int型
2x: float = 42.0      # float型
3x: bool = True       # bool型
4x: str = "test"      # str型
5x: bytes = b"test"   # bytes型

Python3.9+であれば、ビルトイン型で型ヒントできます。

1# 自作クラスをインポート
2from .config import RsyncTaskConfig
3
4task: RsyncTaskConfig | None = RsyncTaskConfig(...)

型ヒントの名前には自作クラスも利用できます。

リスト型したい(list / set / typing.Iterable / typing.Sequence

1# str型のリスト
2x: list[str] = ["test"]
3
4# int型の集合
5x: set[int] = {2, 3, 5, 7, 11}

list[値の型ヒント]set[値の型ヒント]で リスト型(コレクション型)の型ヒントを定義できます。

1from typing import List, Set
2x: List[str] = ["test"]
3x: Set[int] = {2, 3, 5, 7, 11}

Python3.8以前では、ビルトイン型が使えないのでtyping.Listなどを使う必要がありました。

1from typing import Iterable, Sequence
2
3# list-likeな集まり
4x: Iterable[str] = ["test"]
5x: Iterable[int] = {2, 3, 5, 7, 11}
6
7# 読み取り専用のlist-likeな集まり
8x: Sequence[str] = ["test"]
9x: Sequence[int] = {2, 3, 5, 7, 11}

typing.Iterabletyping.Sequencetypingモジュールが提供している型ヒントです。 forループできたり、len__getitem__が利用できるオブジェクトに対する 汎用的なリスト型(list-like)の型ヒントとして利用できます。

注釈

「汎用的な型ヒント」はPythonのDuck Typingに基づいています。 Duck Typingとは、「あひるはガーガー鳴く」という知識から、 「ガーガー鳴いているものはあひる(のようなもの)だ」と推定する考え方です。

この仕組みにより、Pythonではオブジェクトの詳細なクラス設計を知らなくても、 特定のメソッドやプロトコルを持っているかどうかだけで型ヒントを適用できます。

辞書型したい(dict / typing.Mapping / typing.MutableMapping

1x: dict[str, float] = {
2    "field1": 2.0,
3    "field2": 3.0,
4    "field3": 5.0,
5}

dict[キーの型ヒント, 値の型ヒント]で辞書型の型ヒントを定義できます。

 1from typing import Mapping
 2
 3# map-likeな集まり
 4x: Mapping[str, float] = [
 5    "field1": 2.0,
 6    "field2": 3.0,
 7    "field3": 5.0,
 8]
 9
10# mutableなmap-likeな集まり
11x: MutableMapping[str, float] = [
12    "field1": 2.0,
13    "field2": 3.0,
14    "field3": 5.0,
15]

typing.Mappingtyping.MutableMappingtypingモジュールが提供している型ヒントです。 __getitem____setitem__が利用できるオブジェクトに対する 汎用的な辞書型(dict-like)の型ヒントとして利用できます。

タプル型したい(tuple

1# 固定長
2x: tuple[int, str, float] = (3, "yes", 7.5)
3
4# 可変長
5x: tuple[int, ...] = (1, 2, 3, 4, 5)

tuple[値の型ヒント, 値の型ヒント, 値の型ヒント]でタプル型の型ヒントを定義できます。 固定長の場合は、要素数とそれぞれの型ヒントが必要です。 tuple[値の型ヒント, ...]で、同じ型の可変長のタプルを定義できます。

Any型したい(typing.Any

1from typing import Any
2
3x: Any = 42.0
4x: Any = "test"

typing.Anyは「なんでもOK」な特殊型です。 プロトタイピングでとりあえず動かしたいときには便利ですが、 型チェックが無効になるため、長期的に運用する場合は乱用禁止です。

注釈

pyrightには独自拡張のUnknown型があります。 Unknownも任意の型を表しますが、 これはAnyの「なんでもあり」と違い、 「ここは型がわからない」という安全側のコンセプトです。

pyrightはTypeScriptをベースにしているためです。

複数の型ヒントしたい(typing.Union

1# Python3.9まで
2from typing import Union
3x: Union[int, str]
4
5# Python3.10以降
6x: int | str
7x: list[int|str] = [3, 5, "test"]

typing.Unionを使って、複数の型ヒントを組み合わせて指定できます。 Python3.10以降ではOR記号( | )を使ってより簡単に表現できるようになりました。

Noneしたい(typing.Optional

1# Python3.9まで
2typing import Optional
3x: Optional[str]
4
5# Python3.10以降
6x: str | None

Noneを含む場合はtyping.Optionalを使います。 Optional[X]Union[X, None]のシンタックスシュガーです。 Python3.10以降ではOR記号( | ) を使って直感的に表現できるようになりました。

関数したい

 1# 引数なし
 2def 関数名() -> 型ヒント:
 3    ...
 4
 5# 引数あり
 6def 関数名(引数: 型ヒント) -> 型ヒント:
 7    ...
 8
 9# 引数とオプション引数あり
10def 関数名(引数: 型ヒント, 引数: 型ヒント = デフォルト値) -> 型ヒント:
11    ...

関数の戻り値に型ヒントを定義できます。 変数の型ヒントと同様に、ビルトイン型、typingモジュールが提供する型、自作クラスの型など設定できます。

前方参照したい(__future__.annotations

1from __future__ import annotations
2
3def f(foo: A) -> int:
4    ...

__future__.annotationsをインポートすると、クラス名を前方参照を利用できます。

1from typing import TYPE_CHECKING
2
3if TYPE_CHECKING:
4    import bar
5
6def listify(arg: "bar.BarClass") -> "list[bar.BarClass]":
7    return [arg]

typing.TYPE_CHECKINGを使えば、型ヒントだけに自作クラスを適用できます。 循環参照を回避したい場合に利用します。

型ヒントあれこれ

Pythonの「型ヒント」は、あくまでも"注釈(annotation)"として導入された機能です。 これらの型ヒントには強制力がなく、開発支援のための仕組みです。 型チェッカー(mypypyright)やIDE(VS Codeなど)が利用する情報であり、 実行時(ランタイム)には基本的に影響しません。 そのため、既存の関数やクラスの動作を変えることなく、後から型ヒントを追加できます。

より厳密な型チェックやバリデーションを行いたい場合は Pydantic などのパッケージを利用するとよいでしょう。

注釈

最近(2020年)は、TypeScriptやRustに代表されるように静的型付けできる言語がトレンドです。 その波がPythonにもやってきて、関数アノテーションを利用した型ヒントが導入されました。

C/C++初心者のころ(2010年代)、型を宣言するのめんどくさいなとつまずいていたときに、 型とか気にしなくても書けるPython超便利じゃん!と感動し、Pythonでの開発に流れたのに、 1周回ってきてしまった感があります。

リファレンス