Pythonの型を完全に理解するためのtypingモジュール全解説(3.10対応)

# はじめに
Pythonでキチンと型アノテーションを書くのであれば一度は読んでおきたいのが[typingライブラリの公式ドキュメント](https://docs.python.org/ja/3.10/library/typing.html)です。

[前回の記事](https://qiita.com/nicco_mirai/items/c1810ed2a6fc8c53c006)でも読んでくださいと(偉そうに)書いたわけですが、実のところこれは型アノテーションを**解釈する側**[^who]に向けたドキュメントだったりもするのでアノテを書く側にとっては情報がごちゃごちゃしてるんですよね。加えて未翻訳の箇所もそれなりにあってま～～～～読みにくい。実際に読んでいただいた方々からの評判もすこぶる悪かったです。また

> 機能は分かったけど結局どんな時に使えばいいのか分からない

という致命的なコメントも聞きました。
そこで今回はこれらの不満を解消すべく、公式ドキュメントを一般ユーザー向けに再編し具体的なユースケースを盛り込んだ**非公式ドキュメント**をご用意しました。

なおPython 3.10は[2021年10月04日リリース予定](https://www.python.org/dev/peps/pep-0619/)ですが、先駆けてドキュメントが公開されていますので当記事はこちらの**3.10.0b2**バージョンを元に執筆しています。


## 想定読者

- 型アノテーションは書き始めてるけどまだなんとなくふわっとしている
- 最後にtypingのドキュメントを読んでから時間があいている(～3.7くらい)
- 他の言語から来たけどPythonの型ってどうなってるの
- 漸進的型付けやっていくぞ

そもそも型アノテーションとは何ぞやという方はまず[**こちら**](https://gammasoft.jp/blog/python3-function-annotations/)や[**こちら**](https://qiita.com/icoxfog417/items/c17eb042f4735b7924a3)の記事を一読されることをおすすめします。


# 解説

typingモジュールからインポートできるオブジェクトを

- 型アノテーションの拡張
- ユーティリティ

の2つに分類して紹介します。
いかんせん数がはちゃめちゃに多いので読み飛ばせるよう特に重要な項目には★をつけています。


## 型アノテーションの拡張
```py
def hoge(x: int) -> str:
    dx: float = x * 0.1
    return str(dx)
```

上記で言う`int`/`str`/`float`に該当する部分に書くことで型チェッカーに対して様々な情報を伝えることができる特殊な型です。
Python 3.9で多くが非推奨になり(理由は[後述](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#39%E3%81%A7%E9%9D%9E%E6%8E%A8%E5%A5%A8%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%9F%E3%82%82%E3%81%AE))公式ドキュメントが無駄に読み辛くなっているためここでは3.9以降OK/NGに分けて記載します。


### 3.9以降も使えるもの
#### [Any](https://docs.python.org/ja/3.10/library/typing.html#typing.Any)
なんでもいいよ型です。
型に依らない汎用的なライブラリを作る際や型情報のないデータを取ってきて自分でこねこねしないといけない場合に使います。

```py:汎用的な関数の例
from typing import Any, TypeVar
from collections.abc import Iterable

T = TypeVar("T")

# 配列(のようなオブジェクト)の中からt型の要素だけを残して返す関数
# Iterable[Any]はIterableと等価ですが、Anyを書いた方が使う人にやさしいので書いた方がいいです
def type_filter(itr: Iterable[Any], t: type[T]) -> list[T]:
    return [elm for elm in itr if isinstance(elm, t)]

x = ["hoge", 1, "fuga", 2, "piyo", 3]
type_filter(x, str)
>>> ["hoge", "fuga", "piyo"]
```


#### [NoReturn](https://docs.python.org/ja/3.10/library/typing.html#typing.NoReturn)

実行時エラーで値が返らない関数に使います。というか**使いません**。
Pythonは返すものを明示しない場合暗黙的にNoneオブジェクトが返るので`-> NoReturn`と`-> None`は別物であることに注意してください。
`return`を書かない時は`-> None`です。

```py
def hoge() -> None:
    print("hoge!!!")

x = hoge()
>>> hoge!!!
x
>>> None
```


#### ★ [Union](https://docs.python.org/ja/3.10/library/typing.html#typing.Union)
```py:3.9までの合併型
from typing import Union

hoge: Union[int, str]
```

とした場合`hoge`は`int`/`str`両方の値を取り得ます。「もしくは」です。
3.10からは`|`演算子の適用が拡張されて

```py:3.10からの合併型
hoge: int | str
```

と書けるようになりますが、これは

- 見た目がスマート
- パッと見で意味が分かる(論理和なので)
- `Union`をインポートする必要がない
- TypeScriptやScalaと同じ書き方

と良いことずくめなので是非`Union`から乗り換えて欲しいなと思います。


#### ★ [Optional](https://docs.python.org/ja/3.10/library/typing.html#typing.Optional)
```py
hoge: Union[int, None]
```

を省略した形が

```py
hoge: Optional[int]
```

です。そんなに省略できてなくて面白いですね[^opt]。
そしてこの略記というものがチーム開発をする上では非常にやっかいで、上記[Union](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-union)の略記もあわせると

```py:Null許容型の表記いろいろ
hoge: Union[int, None]
hoge: int | None
hoge: Optional[int]
```

と3パターンもの表記ゆれが発生することになります。特にNull許容型は発生頻度も高いのでプロジェクトのコーディング規約でどれを使うのか定めておきましょう。

なお公式ドキュメントではダメだよと言われている

```py:xの型が不一致
def hoge(x: int = None) -> None:
    ...
```

この書き方ですが、どうやら[3.10以降`inspect.signature()`はこれを`Optional[int]`と見なしてくれる](https://qiita.com/tk0miya/items/c6232ab687df5e561faa)らしく、型チェッカーも追随してくれないかなぁとひそかに期待しています。


#### ★ [Literal](https://docs.python.org/ja/3.10/library/typing.html#typing.Literal)
ブラケットの中で指定したリテラル(文字列/整数)[^lit]のみを受け入れます。

```py
from typing import Literal

x: Literal["hoge", "fuga", "piyo"]
x = "hoge"  # OK
x = "foo"  # NG
```

TSのuinon型ほど万能ではありませんが大抵の[Enum](https://docs.python.org/ja/3.10/library/enum.html#creating-an-enum)を置換できるため非常に強力です。
Enumと違って反復処理ができない(`x`から`"hoge"`/`"fuga"`/`"piyo"`を取り出すことができない)のが弱点。


#### ★ [TypeGuard](https://docs.python.org/ja/3.10/library/typing.html#typing.TypeGuard) (3.10 ～)
要はTSの[**アレ**](https://book.yyts.org/features/type-guards#type-predicate)なのですが説明が難しいので1から解説します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245527/b2ecbbdf-6063-efe8-f04c-8887fae5ee9d.png)
11行目でエラーが出ていますね。
`l`の中身は`is_str_list`関数によって`str`型であることが確定しているので我々人間からするといいんじゃないの？と思うのですが、型チェッカーくんは**自作の型判定関数を理解しません**。普段`isinstance`で型の絞り込みが行えているのは型チェッカーが`isinstance`や`issubclass`に対して特別なプログラムを組んでいるからなのです[^type]。

ということで`TypeGuard`の出番なのですが…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245527/65a54074-c9b6-b78c-1784-95ecf9ca65db.png)
ちょっと何言ってるか分からないですね。
押さえるべき`TypeGuard`の仕様は3つです。

1. `TypeGuard`を使う関数は引数を1つだけ取る
1. この関数は実際には`bool`値を返す
1. この関数が`True`を返した場合、型チェッカーは渡された引数が`TypeGuard`のブラケット内に書かかれた型であると判定する

よって型チェッカーが`l`の中身(`elm`)を`str`型であると認識しエラーが消えたということになります。

```py:コピペ用(画像と同じコードです)
from typing import Any, TypeGuard

def is_str_list(l: list[Any]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in l)

def hoge(l: list[int | str]):
    if is_str_list(l):
        for elm in l:
            print(elm.lower())
```



#### ★ [Final](https://docs.python.org/ja/3.10/library/typing.html#typing.Final)
`Final`がついた変数には再代入ができなくなります(強制的に定数になります)。

```py
HOSHII_YEN: Final = 5000_0000_0000_0000
HOSHII_YEN += 1  # NG
```

クラス定数をサブクラスでオーバーライドすることも禁止されます。

```py
class Hoshii:
    YEN: Final[int] = 5000_0000_0000_0000

class MottoHoshii(Hoshii):
    YEN = 5000_0000_0000_0000_0000  # NG
```


#### [Annotated (3.9～)](https://docs.python.org/ja/3.10/library/typing.html#typing.Annotated)

事実上アノテーションの機能が型アノテーションに支配されたので、型以外のアノテーションもつけれる方法を再定義しようぜ、ということで生まれた構文です。

```py
hoge: Annotated[type, x, y, ...]
```

とすることで`hoge`という**名前**に対して型と後続のメタデータ(なんでも可、長さも自由)を付与することができるようになります。
型チェッカーが付与されたメタデータを元にいろいろ警告を出してくれるようになったりすると便利なんでしょうね。
今のところコメント以上の意味はありません🙃


#### ★ プロトコル(一覧)
これらの型は通常の型(公称型)と異なり**特定のメソッドが実装されているかどうか**を元に型チェッカーが型の整合を判断します。(→[Protocol](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-protocol))

```py
from typing import Iterable, SupportsRound
from decimal import Decimal

def round_sum(nums: Iterable[SupportsRound]) -> int:
    return sum(round(num) for num in nums)

round_sum([1, 1.3, Decimal("1.4")])  # これらの型に親子関係は存在しないが全員`__round__`メソッドを持つのでOK
>>> 3
```

| プロトコル | 対応するメソッド | 備考 |
| ---- | ---- | ---- |
[SupportsBytes](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsBytes) | `__bytes__`
[SupportsInt](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsInt) | `__int__`
[SupportsFloat](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsFloat) | `__float__`
[SupportsComplex](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsComplex) | `__complex__`
[SupportsAbs](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsAbs) | `__abs__` | 戻り値の型は共変
[SupportsRound](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsRound) | `__round__` | 戻り値の型は共変
[SupportsIndex](https://docs.python.org/ja/3.10/library/typing.html#typing.SupportsAbs) | `__index__`

`__add__`などのプロトコルを自分で作りたい場合は同様に`SupportsAdd`のような名前にすると統一感があっていいと思います。
また[**コレクションのABC**](https://docs.python.org/ja/3.10/library/collections.abc.html#collections-abstract-base-classes)はABCでありながらプロトコルと同様に扱うことが可能です。

```py
from collections.abc import Sequence

def count_hoge(seq: Sequence[str]) -> int:  # Sequenceは破壊的メソッドを持たないため渡したseqに副作用が起こらないことを保証できる
    return seq.count("hoge")

count_hoge(["hoge", "fuga", "piyo"])  # OK
count_hoge([1, 2, 3])  # NG
count_hoge("hoge")  # OK(str型はSequence[str]でもあるので)
```


### 3.9で非推奨になったもの
「`int`の`list`」ように「`型`の`型'`」で表せる`型'`のことを**ジェネリック型**(総称型/汎用型)と言いますが、以前はこのジェネリックを型チェッカーへ通知するためにtypingからジェネリック専用の型をインポートする必要がありました。

```py:3.8以前
from typing import List

any_list: list = [1, 2, "3"]  # OK
int_list: List[int] = [1, 2, 3]  # OK
str_list: list[str] = ["1", "2", "3"]  # NG
```

ただこれはもう今の時代にそぐわなかろう、ということで3.9からはいよいよ元の型にも`[]`(正確に言うと[\_\_class_getitem\_\_](https://docs.python.org/ja/3.10/reference/datamodel.html#object.__class_getitem__))がサポートされます。

```py:3.9以降
int_list: list[int] = [1, 2, 3]  # OK!!
```

役目を終えたtypingのジェネリック専用型たちは**Python3.9のリリース(2020/10/05)から5年後のバージョンで削除されることが決まっている[^5y]**ので、今のうちに置き換えておきましょう。


#### ★ [Tuple](https://docs.python.org/ja/3.10/library/typing.html#typing.Tuple) / [List](https://docs.python.org/ja/3.10/library/typing.html#typing.List) / [Dict](https://docs.python.org/ja/3.10/library/typing.html#typing.Dict) / [Set](https://docs.python.org/ja/3.10/library/typing.html#typing.Set) / [FrozenSet](https://docs.python.org/ja/3.10/library/typing.html#typing.FrozenSet)

それぞれbuiltinsの`tuple` / `list` / `dict` / `set` / `frozenset`に相当。
アノテーションの書き方が少し複雑なのでいくつか具体例を書きます。

```py:このコードはちゃんと動きます(3.10以降)
int型の配列: list[int] = [1, 2, 3]
int型またはstr型の配列: list[int | str] = [1, 2, "3"]
int型3つのタプル: tuple[int, int, int] = (1, 2, 3)
int型int型str型のタプル: tuple[int, int, str] = [1, 2, "3"]
int型がいくつか入ったタプル: tuple[int, ...] = (1, 2, 3)
空のタプル: tuple[()] = ()
str型がkeyでint型をvalueとする辞書: dict[str, int] = {"alice": 1, "bob": 2, "carol": 3}
# 「1つ目の要素がint型、2つ目以降の要素がstr型の長さが不明なタプル」のようなアノテーションは書けない
# tuple[int, str, ...] ← これはダメ
# なのでその場合はこう↓
int型とstr型配列のタプル: tuple[int, list[str]] = (1, ["2", "3"])
```

またちょっと特殊な使い方として`Generic[T]`は**継承**ができます。
例えば自作オブジェクトの配列に属性をつけたい時ってあるじゃないですか？ああいう時に

```py
from typing import NamedTuple

class Line(NamedTuple):
    min: int
    max: int

class Lines(list[Line]):
    @property
    def bbox(self) -> Line:
        return Line(min(l.min for l in self), max(l.max for l in self))
```

このような宣言を書くことによって`Lines`のインスタンスには`Line`以外の要素を追加できなくなります。
もちろん特別な属性がいらない場合は

```py:TypeAliasは3.10から
Lines: TypeAlias = list[Line]
```

でいいので登場機会は少ないですが覚えておいて損はないです。(→[Generic](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-generic)、[TypeAlias](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#typealias-310-))


#### ★ [Callable](https://docs.python.org/ja/3.10/library/typing.html#typing.Callable)
[collections.abc](https://docs.python.org/ja/3.10/library/collections.abc.html#collections-abstract-base-classes)の[**Callable**](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Callable)に相当。
関数を受け取ったり返したりする関数(=高階関数)を作るとき、最もメジャーな例で言うと**デコレータ**を作るときに使います。

```py:3.9までのデコレータ
from typing import TypeVar
from collections.abc import Callable
from functools import wraps

R = TypeVar("R")

def with_good_morning(func: Callable[..., R]) -> Callable[..., R]:
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("おはよー！")
        return func(*args, **kwargs)

    return wrapper

@with_good_morning
def greet(*greets: str) -> None:
    for g in greets:
        print(g)
    print("おやすみー！")

@with_good_morning
def greet_(afternoon: str, evening: str) -> None:
    print(afternoon)
    print(evening)
    print("おやすみー！")

greet("こんちわー！", "こんばんわー！")
>>> おはよー！
>>> こんちわー！
>>> こんばんわー！
>>> おやすみー！
greet_("こんちわー！", "こんばんわー！")
>>> おはよー！
>>> こんちわー！
>>> こんばんわー！
>>> おやすみー！
```

ただ我々のような型愛好家にとってデコレータは長らく鬼門でありました。
なぜなら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245527/cbd82712-98d4-a001-4e4c-65de7991afbc.png)
デコレータをつけた関数は外部から**引数の情報が見えなくなってしまう**のです。
だからといってデコレータのアノテーションを

```py
def with_good_morning(func: Callable[[P], R]) -> Callable[[P], R]:
    ...
```

にしては`greet`と`greet_`のように引数の数が異なる関数に適用できなくなってしまいます。
そこで、3.10では以下の新しい仕組みが導入されました。


##### ★ [ParamSpec](https://docs.python.org/ja/3.10/library/typing.html#typing.ParamSpec) (3.10 ～)
引数のための特別な[型変数](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-typevar)です。

```py:3.10からのデコレータ
from typing import TypeVar, ParamSpec
from collections.abc import Callable
from functools import wraps

P = ParamSpec("P")
R = TypeVar("R")

def with_good_morning(func: Callable[P, R]) -> Callable[P, R]:
    @wraps
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print("おはよー！")
        return func(*args, **kwargs)

    return wrapper

@with_good_morning
def greet(*greets: str) -> None:
    for g in greets:
        print(g)
    print("おやすみー！")

@with_good_morning
def greet_(afternoon: str, evening: str) -> None:
    print(afternoon)
    print(evening)
    print("おやすみー！")
```

これで引数が見れるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245527/9f1e4883-db2d-aa5e-6577-33f4d1b79d8d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245527/76dc074e-9686-621c-b314-db7fe96faa66.png)
悲願成就！
ちなみに[**functools.wraps**](https://docs.python.org/ja/3.10/library/functools.html#functools.wraps)については[こちらの記事](https://qiita.com/moonwalkerpoday/items/9bd987667a860adf80a2)に詳しいです。


##### [Concatenate](https://docs.python.org/ja/3.10/library/typing.html#typing.Concatenate) (3.10 ～)
`ParamSpec`を*Concatenate*(＝結合)します。

```py
def with_good_morning(func: Callable[Concatenate[Logger, P], R]) -> Callable[P, R]:
    ...
```

とかなんとかすることによってデコレートした関数の引数を減らしたり増やしたりできるんですが、私はそもそもそういう目的でデコレータを使うべきではないと思っている[^deco]ので使いません。


#### [Type](https://docs.python.org/ja/3.10/library/typing.html#typing.Type)
builtinsの`type`に相当。
[Any](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#any)の説明でちょろっと使いましたがあんな感じで**型そのもの**を受け入れる場合に使います。

```py:int型の値を受け入れるとき
def hoge(t: int):
    ...
```

```py:int型を受け入れるとき
def hoge(t: type[int]):
    ...
```

`int`型の～と書きましたが、実際にはどちらも**intとして扱えるもの**が対象なので例えば前者には`True`を、後者には`bool`を渡すことも可能です(関数内では`int`(型)として扱われます)。
受け取った型を戻り値に使いたい場合にはこうなります。

```py:intを渡すとint値が、boolを渡すとbool値が返る。strは渡せない
IntT = TypeVar("IntT", bound=int)

def make_int(t: type[IntT]) -> IntT:
    ...
```

(→[TypeVar](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-typevar))


#### [Iterator](https://docs.python.org/ja/3.10/library/typing.html#typing.Iterator) / [Generator](https://docs.python.org/ja/3.10/library/typing.html#typing.Generator)
[collections.abc](https://docs.python.org/ja/3.10/library/collections.abc.html#collections-abstract-base-classes)の[**Iterator**](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Iterator) / [**Generator**](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Generator)に相当。
ジェネレータのアノテーションは`Generator[yieldで返す値の型, sendで受ける値の型, returnで返す値の型]`ですが、`send`も`return`も使わない場合には`Generator[yieldで返す値の型, None, None]`となり、これは事実上`Iterator[yieldで返す値の型]`と同じです。
どちらで書くかは好みのレベルですがプロジェクト内でバラバラになると気持ち悪いので規約なりで定めておくといいでしょう[^itr]。

```py:みんな大好きフィボナッチ数列ジェネレータ
from collections.abc import Generator, Iterator

def fibonacci_max(max: int) -> Generator[int, None, str]:
    a, b = 0, 1
    while a <= max:
        yield a
        a, b = b, b + a
    return "done"

def fibonacci_n(n: int) -> Iterator[int]:  # -> Generator[int, None, None] でも○
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, b + a
```


#### その他(一覧)

| 非推奨の型 | 対応する型 | 備考 |
| ---- | ---- | ---- |
[ChainMap](https://docs.python.org/ja/3.10/library/typing.html#typing.ChainMap) | [collections.ChainMap](https://docs.python.org/ja/3.10/library/collections.html#chainmap-objects)
[DefaultDict](https://docs.python.org/ja/3.10/library/typing.html#typing.DefaultDict) <br> [OrderedDict](https://docs.python.org/ja/3.10/library/typing.html#typing.OrderedDict) <br> [Counter](https://docs.python.org/ja/3.10/library/typing.html#typing.Counter) | [collections.defaultdict](https://docs.python.org/ja/3.10/library/collections.html#collections.defaultdict) <br> [collections.OrderedDict](https://docs.python.org/ja/3.10/library/collections.html#collections.OrderedDict) <br> [collections.Counter](https://docs.python.org/ja/3.10/library/collections.html#collections.Counter) | `dict`のサブクラス3兄弟。 <br> `Counter`はKey-VauleのVが`int`で固定されているため`Counter[KT]`。
[Deque](https://docs.python.org/ja/3.10/library/typing.html#typing.Deque) | [collections.deque](https://docs.python.org/ja/3.10/library/collections.html#deque-objects)
[Pattern](https://docs.python.org/ja/3.10/library/typing.html#typing.Pattern) <br> [Match](https://docs.python.org/ja/3.10/library/typing.html#typing.Match) | [re.Pattern](https://docs.python.org/ja/3.10/library/re.html#regular-expression-objects) <br> [re.Match](https://docs.python.org/ja/3.10/library/re.html#match-objects) | これらが持っている値は[AnyStr](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#anystr)なので`Match[T]`でどちらなのか明示できます。
[Container](https://docs.python.org/ja/3.10/library/typing.html#typing.Container) | [collections.abc.Container](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Container)
[Sized](https://docs.python.org/ja/3.10/library/typing.html#typing.Sized) | [collections.abc.Sized](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Sized)
[Collection](https://docs.python.org/ja/3.10/library/typing.html#typing.Collection) | [collections.abc.Collection](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Collection)
[ByteString](https://docs.python.org/ja/3.10/library/typing.html#typing.ByteString) | [collections.abc.ByteString](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.ByteString) | `bytes`のスーパークラス。
[Sequence](https://docs.python.org/ja/3.10/library/typing.html#typing.Sequence) <br> [MutableSequence](https://docs.python.org/ja/3.10/library/typing.html#typing.MutableSequence) | [collections.abc.Sequence](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Sequence) <br> [collections.abc.MutableSequence](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.MutableSequence)| `list`のスーパークラス。
[AbstractSet](https://docs.python.org/ja/3.10/library/typing.html#typing.AbstractSet) <br> [MutableSet](https://docs.python.org/ja/3.10/library/typing.html#typing.MutableSet) | [collections.abc.Set](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Set) <br> [collections.abc.MutableSet](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.MutableSet) | `set`のスーパークラス。
[Mapping](https://docs.python.org/ja/3.10/library/typing.html#typing.Mapping) <br> [MutableMapping](https://docs.python.org/ja/3.10/library/typing.html#typing.MutableMapping) | [collections.abc.Mapping](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Mapping) <br> [collections.abc.MutableMapping](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.MutableMapping) | `dict`のスーパークラス。
[MappingView](https://docs.python.org/ja/3.10/library/typing.html#typing.MappingView) <br> [ItemsView](https://docs.python.org/ja/3.10/library/typing.html#typing.ItemsView) <br> [KeysView](https://docs.python.org/ja/3.10/library/typing.html#typing.KeysView) <br> [ValuesView](https://docs.python.org/ja/3.10/library/typing.html#typing.ValuesView) | [collections.abc.MappingView](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.MappingView) <br> [collections.abc.ItemsView](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.ItemsView) <br> [collections.abc.KeysView](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.KeysView) <br> [collections.abc.ValuesView](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.ValuesView)
[Iterable](https://docs.python.org/ja/3.10/library/typing.html#typing.Iterable) | [collections.abc.Iterable](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Iterable)
[Hashable](https://docs.python.org/ja/3.10/library/typing.html#typing.Hashable) | [collections.abc.Hashable](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Hashable)
[Reversible](https://docs.python.org/ja/3.10/library/typing.html#typing.Reversible) | [collections.abc.Reversible](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Reversible)
[Awaitable](https://docs.python.org/ja/3.10/library/typing.html#typing.Awaitable) | [collections.abc.Awaitable](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Awaitable)
[Coroutine](https://docs.python.org/ja/3.10/library/typing.html#typing.Coroutine) | [collections.abc.Coroutine](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.Coroutine) | コルーチンのアノテーションは[ジェネレータ](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#iterator--generator)と同じ。
[AsyncIterable](https://docs.python.org/ja/3.10/library/typing.html#typing.AsyncIterable) | [collections.abc.AsyncIterable](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.AsyncIterable)
[AsyncIterator](https://docs.python.org/ja/3.10/library/typing.html#typing.AsyncIterator) <br> [AsyncGenerator](https://docs.python.org/ja/3.10/library/typing.html#typing.AsyncGenerator) | [collections.abc.AsyncIterator](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.AsyncIterator) <br> [collections.abc.AsyncGenerator](https://docs.python.org/ja/3.10/library/collections.abc.html#collections.abc.AsyncGenerator) | 非同期ジェネレータのアノテーションは基本[ジェネレータ](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#iterator--generator)と同じですが <br> ジェネレータと違って値を返せないので`return`部分がない。
[AsyncIterator](https://docs.python.org/ja/3.10/library/typing.html#typing.ContextManager) <br> [AsyncContextManager](https://docs.python.org/ja/3.10/library/typing.html#typing.AsyncContextManager) | [contextlib.AbstractContextManager](https://docs.python.org/ja/3.10/library/contextlib.html#contextlib.AbstractContextManager) <br> [contextlib.AbstractAsyncContextManager](https://docs.python.org/ja/3.10/library/contextlib.html#contextlib.AbstractAsyncContextManager)



## ユーティリティ
型にまつわる様々な便利機能を提供してくれるオブジェクトたちです。


### ★ [TypeVar](https://docs.python.org/ja/3.10/library/typing.html#typing.TypeVar)
型を入れる変数、型変数です。
これを引数の型に指定することで**呼び出し時に型が決まる**汎用的(＝ジェネリック)な関数/クラスを作ることができます。

```py:基本的な宣言
from typing import TypeVar

T = TypeVar("T")  # なんでも取れる
S = TypeVar("S", str, int)  # strかintを取れる
IntT = TypeVar("IntT", bound=int)  # intかintのサブクラスを取れる
```


```py:合併型(Union)との違い
from typing import TypeVar, TypeAlias

T = TypeVar("T", str, int)
U: TypeAlias = str | int

def hoge(x: T, y: T):  # ジェネリック関数
    ...

def fuga(x: U, y: U):  # 普通の関数
    ...

hoge(0, 0)  # OK
fuga(0, 0)  # OK
hoge("", "")  # OK
fuga("", "")  # OK
hoge(0, "")  # NG!!
fuga(0, "")  # OK
```

共変や反変な型を使いたい場合はそれぞれ`covariant`/`contravariant`に`True`を渡します[^co]。

```py:共変/反変の型変数の命名規則についてはPEP484に小さく書かれていますが要はハンガリアンしろとのことです
from typing import TypeVar
 
T_co = TypeVar("T_co", covariant=True)
T_contra = TypeVar("T_contra", contravariant=True)
```


#### [AnyStr](https://docs.python.org/ja/3.10/library/typing.html#typing.AnyStr)
```py
AnyStr == TypeVar("AnyStr", str, bytes)
```

です。なんかよく使うから用意したらしい。



### [TypeAlias](https://docs.python.org/ja/3.10/library/typing.html#typing.TypeAlias) (3.10 ～)
複雑になってしまったアノテーションにはエイリアスを設定できるのですが、[その書き方がグローバル変数への代入と区別がつき辛いという意見](https://www.python.org/dev/peps/pep-0613/)がありエイリアスをキチンと明示するために導入されました。

```py:
from typing import TypeAlias

Mutables = [list, set, dict]  # グローバル変数への代入
Mutable: TypeAlias = list | set | dict  # 型エイリアス

def hoge(m: Mutable):
    ...
```

ちなみにこの型エイリアスも**ジェネリックにできます**。
一番わかりやすい例として[Optional](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-optional)(のようなもの)を自前で実装してみましょう。

```py
from typing import TypeVar, TypeAlias

T = TypeVar("T")
Optional: TypeAlias = T | None

def hoge(x: Optional[int] = None):
    ...

def fuga(x: Optional[T] = None) -> T:  # エイリアスを使ってジェネリック関数も作れる
    ...
```

とは言え所詮エイリアスはエイリアスであって型ではない(つまり`isinstance`による判定ができない)ので過信は禁物です。あくまでも簡易的な方法として。


### [NewType](https://docs.python.org/ja/3.10/library/typing.html#typing.NewType)
サブクラスのように振舞うサブクラスではない**なにか**を生み出します。
得られる計算コストの減少量に対してコードの複雑化というデメリットが大きすぎるので素直にサブクラスを作ってください。


### ★ [Generic](https://docs.python.org/ja/3.10/library/typing.html#typing.Generic)
ジェネリックな型を1から自作したい時に継承するABCです。

```py:究極まで単純化したジェネリック型
from typing import Generic, TypeVar
from dataclasses import dataclass

T = TypeVar("T")

@dataclass
class Hoge(Generic[T]):
    x: T

    def hoge(self, t: T) -> T:
        ...

hoge: Hoge[int] = Hoge(0)  # OK
hoge_: Hoge[str] = Hoge(0)  # NG
hoge.hoge(0)  # OK
hoge.hoge("")  # NG
```

`Generic[T]`を継承するという一見ぎょっとするような書き方ですが、なんとこの継承方法は全てのジェネリック型で共通です。
またその際**型変数に型変数を代入**することも出来るので

```py
from typing import TypeVar

T = TypeVar("T")

# collections.Counterと同じ
class Counter(dict[T, int]):
    ...

x: Counter[str]
x = Counter({"hoge": "h", "fuga": "f", "piyo": "p"})  # NG
x = Counter({"hoge": 1, "fuga": 2, "piyo": 3})  # OK
```

このように型の一部だけを固定、ということが可能です。


### ★ [Protocol](https://docs.python.org/ja/3.10/library/typing.html#typing.Protocol)
[**構造的部分型**](https://book.yyts.org/features/structural-subtyping)を定義するための基底クラスです。

```py
from typing import Protocol

class Greeter(Protocol):
    def greet(self, name: str) -> str:
        ...

class Caller:
    def greet(self, name: str) -> str:
        return f"{name}！おはよー！"

def greet(g: Greeter) -> None:
    print(g.greet("alice"))

greet(Caller())  # OK
```

`Greeter`と`Caller`に直接の継承関係はありませんが、`Greeter`が`Protocol`を継承していることによって型チェッカーは`Caller`を`Greeter`であると見なせるようになります。
(それがなぜ嬉しいのかについては[前回の記事](https://qiita.com/nicco_mirai/items/c1810ed2a6fc8c53c006#protocol-38)を読んでいただけると嬉しいです。)

もう少し具体的に説明するとプロトコルは

- メソッド名
- 引数名
- 引数の型
- 戻り値の型

の4つが一致するメソッドを持つクラスを派生型と見なします[^sig]。よって

```py
class Repeater:
    def greet(self, n: int) -> str:
        return "おはよー！" * n

greet(Repeater())  # NG
```

引数名と引数の型が異なるこれはダメです。
ただし逆に言えばメソッド名が一致していても他の3つが一致しなければ別物として扱われる(関係ないクラスを巻き込まない)ので、[PEP544](https://www.python.org/dev/peps/pep-0544/#callback-protocols)にあるように複雑なコールバック関数を**複雑な\_\_call__メソッドを持つプロトコル**として定義してしまうことも可能です。

```py
from typing import Protocol, Optional

class Combiner(Protocol):
    def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]:
        ...

def hoge(cb: Combiner):  # これをCallableで表現しようと思ったら大変
    ...
```

(→[Callable](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-callable))


#### [@runtime_checkable](https://docs.python.org/ja/3.10/library/typing.html#typing.runtime_checkable)
実行時にチェックできる。
プロトコルを`@runtime_checkable`でデコレートすると`isinstance`/`issubclass`が反応するようになります。

```py
from typing import Protocol, runtime_checkable

@runtime_checkable
class Greeter(Protocol):
    def greet(self, name: str) -> str:
        ...

class Caller:
    def greet(self, name: str) -> str:
        return f"{name}！おはよー！"

issubclass(Caller, Greeter)
>>> True
```

注意点がありまして、型チェッカーはメソッドのシグネチャ全ての一致を確認するのに対してプロトコルに対する*runtime_check*はメソッド名の一致しか確認しません。よって引数名と引数の型が異なる`Repeater`は

```py
class Repeater:
    def greet(self, n: int) -> str:
        return "おはよー！" * n

issubclass(Repeater, Greeter)
>>> True
```

`Greeter`の派生型になります。**大嘘です**。なんか上手く言葉にできないんですが、構造的部分型ってそういう風に使うものじゃないような気がするのでこれでいいんだと思います…。
そしてそういう感じなので私は`@runtime_checkable`を**使わない**ことをおすすめします。


### [ClassVar](https://docs.python.org/ja/3.10/library/typing.html#typing.ClassVar)
```py
from typing import ClassVar

class Hoge:
    x: ClassVar[int] = 1  # これはクラス変数！
    y: int  # こっちはインスタンス変数
```
こんな感じでクラスを定義するとインスタンスで`x`の値を変更しようとした際にエラーを出してくれます。

```py
hoge = Hoge()
hoge.y = 1  # OK
hoge.x = 2  # NG
Hoge.x = 3  # OK
```

**と**、いうのが本来の(公式ドキュメントに書かれている)目的なのですが、私は事実上[**@dataclass**](https://docs.python.org/ja/3.10/library/dataclasses.html#dataclasses.dataclass)にクラス変数を通知するためのものだと思っています。

```py:datacalssが作る諸々のメソッドにClassVarは含まれない
from typing import ClassVar
from dataclasses import dataclass

@dataclass
class Hoge:
    x: ClassVar[int] = 1
    y: int

hoge = Hoge(3)
hoge.x
>>> 1
hoge.y
>>> 3
```

大変かしこいですね[^smt]。


### ★ [NamedTuple](https://docs.python.org/ja/3.10/library/typing.html#typing.NamedTuple)
[**collections.namedtuple**](https://docs.python.org/ja/3.10/library/collections.html#collections.namedtuple)という**名前で要素にアクセスできる**`tuple`に型を追加して最強になった姿です。

```py
from typing import NamedTuple

class RGB(NamedTuple):
    r: int
    g: int
    b: int

RGB("hoge", "fuga", "piyo")  # NG
saxe_blue = RGB(65, 139, 137)  # OK
saxe_blue[0]
>>> 65
saxe_blue.b
>>> 137
```

面倒なクラス宣言をすっ飛ばせて大変便利ですがあくまでも`tuple`なので`tuple`が欲しい時だけ使うようにしましょう。
`tuple`である必要がないのであれば[**@dataclass**](https://docs.python.org/ja/3.10/library/dataclasses.html#dataclasses.dataclass)で十分です。


### [TypedDict](https://docs.python.org/ja/3.10/library/typing.html#typing.TypedDict)
項目の存在と型を型チェッカーによって強制される`dict`を作ります。
でもそれって悪意のある言い方をすれば**属性へのアクセスが面倒になった構造体**だと思うんですけど…

```py
from typing import TypedDict
from dataclasses import dataclass

class Point2D(TypedDict):  # 実行時はdictになる
    x: int
    y: int
    label: str

@dataclass
class Point3D:
    x: int
    y: int
    z: int
    label: str

td: Point2D
td = {"x": 1, "y": 2}  # NG
td = {"x": 1, "y": 2, "label": 3}  # NG
td = {"x": 1, "y": 2, "label": "ok"}  # OK
td = Point2D(1, 2, "ng")  # NG
td = Point2D(x=1, y=2, label="TypedDict")  # OK
td["label"]
>>>TypedDict
dc: Point3D
dc = Point3D(1, 2, 3, "ok")  # OK
dc = Point3D(x=1, y=2, z=3, label="dataclass")  # OK
dc.label
>>>dataclass
```

一応`TypedDict`はコンストラクタ引数が強制的にキーワード専用になること、`dict`のメソッドを扱えること、**dictが持つ属性以外の属性が存在しないことを保証できること**が[**@dataclass**](https://docs.python.org/ja/3.10/library/dataclasses.html#dataclasses.dataclass)にはないメリットですが、このメリットが美味しく見える場面はかなり限定的です。
使い分けを意識しなければいけない億劫さを考えると一律**使わない**で問題ないと思います。


### [cast](https://docs.python.org/ja/3.10/library/typing.html#typing.cast)
どうしても正規の方法では型チェッカーに型を認識させられない場合の緊急避難として変数の型情報を上書きするための関数でした。
3.10からは[**TypeGuard**](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-typeguard-310-)を使うので`cast`の使用は**禁止**しましょう。

```py:TypeGuardがない時代にはこうする必要がありました
from typing import Any, cast

def is_str_list(l: list[Any]) -> bool:
    return all(isinstance(x, str) for x in l)

def hoge(l: list[int | str]):
    if is_str_list(l):
        for elm in l:
            elm = cast(str, elm)  # ここで型チェッカーから見たelmの型がint | strからstrに変化する
            print(elm.lower())
```


### ★ [@overload](https://docs.python.org/ja/3.10/library/typing.html#typing.overload)
なんちゃってオーバーロードです。
[合併型](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-union)や[型変数](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-typevar)では対応しきれないような複雑な引数と戻り値の組み合わせを持つ関数にはこっちを使います。

```py
from typing import overload

@overload
def hoge() -> None:
    ...

@overload
def hoge(x: int) -> tuple[int, str]:
    ...

@overload
def hoge(x: bytes) -> str:
    ...

def hoge(x=None):  # 実際の分岐はダラダラやるしかない(_hoge_int()/_hoge_bytes()みたいな関数を作ってもいいですが…)
    if x is None:
        return x
    if isinstance(x, int):
        return x, str(x)
    return str(x)
```

エディタで`hoge`関数を見てみると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245527/1e0956e9-013e-6557-ea35-d75bdb294b5d.png)
キチンと3パターンの情報が載っています。


### [@final](https://docs.python.org/ja/3.10/library/typing.html#typing.final)
[**Final**](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-final)のデコレータ版です。
メソッドのオーバーライドとクラスの継承を禁止します。

```py
from typing import final

class Hoge:
    @final
    def hoge(self):  # @finalのついたメソッドをオーバーライドすることは出来ない
        ...

class HogeHoge(Hoge):
    def hoge(self):  # NG
        ...

@final
class Fuga:  # @finalのついたクラスを継承することは出来ない
    ...

class FugaFuga(Fuga):  # NG
    ...
```


### [@no_type_check](https://docs.python.org/ja/3.10/library/typing.html#typing.no_type_check) / [@no_type_check_decorator](https://docs.python.org/ja/3.10/library/typing.html#typing.no_type_check_decorator)
型ではないアノテーションを書きたい時に使うものだったので3.9以降は使いません。
代わりに → [**Annotated**](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#annotated-39)


### [@type_check_only](https://docs.python.org/ja/3.10/library/typing.html#typing.type_check_only)
スタブファイル(.pyi)にプライベートクラスを書くために使います(.pyの中では使えません)。
公式ドキュメントにもある通りこのデコレータを使うような状況はそもそも設計として推奨されないので存在自体を忘れてしまって大丈夫です。


# おわりに

以上[**Introspection helpers**](https://docs.python.org/ja/3.10/library/typing.html#introspection-helpers)(型アノテーションを解釈するためのヘルパー関数群)を除いた全オブジェクトの解説でした。
Pythonの型システムは3.9を迎えてほぼ完成したなと去年は思っていたのですが、こうしてまとめてみると3.10でも大きく進化していることが分かります。特に[**ParamSpec**](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-paramspec-310-)や[**TypeGuard**](https://qiita.com/nicco_mirai/items/5591c1c48b1422c324c9#-typeguard-310-)などは待望だった方も多いのではないでしょうか？

使わないものをバッサリ切ったりでかなり私見(というか思想)の強い解説にはなっていますが、この記事が世のPythonコードを堅牢に、そして**綺麗**にすることに貢献できれば幸いです。



[^who]: 型チェッカーとかあるいはそういったライブラリを作る人々
[^opt]: 普通に`int?`じゃダメだったんでしょうか？
[^lit]: その他に`True`/`False`/`None`を使うことが出来ますが解説が蛇足っぽかったので割愛
[^5y]: [PEP 585](https://www.python.org/dev/peps/pep-0585/)より引用; *The deprecated functionality will be removed from the typing module in the first Python version released 5 years after the release of Python 3.9.0.*
[^deco]: コードが複雑になるので
[^itr]: 一応参考程度に私は`Iterator[yield]`派です
[^smt]: それはそうとアノテーションが実行に影響を与えるのは思想的にどうなんだろうという疑問はあります
[^type]: なのでよく見る`if type(hoge) is type:`も型の絞り込みには使えません
[^sig]: 公式ドキュメントではこれをシグネチャと呼んでいるのでPythonの言うシグネチャには戻り値の型も含まれるということが分かりました
[^co]: ここの解説を入れるとそれだけで1記事分になってしまうのですみませんが省略させてください