Python Unionとは?複数の型を許容する型ヒントの書き方
PythonのUnionは、「複数の型のうちどれか」を表す型ヒントです。
「数値でも文字列でも受け取れる関数」のように、入力の型が1つに定まらない場面で使います。
Unionの基本的な使い方
from typing import Union
def to_int(v: Union[int, str]) -> int:
return int(v)
print(to_int(5))
print(to_int("5"))
実行結果:
5
5
Union[int, str]は「intかstr」という意味です。3つ以上も並べられます。
Union[int, float, str]
想定外の型を弾いてくれる
# a4.py
from typing import Union
def to_int(v: Union[int, str]) -> int:
return int(v)
to_int(1.5)
実行自体は通ってしまいます(int(1.5)は1)。しかし型チェッカーは指摘します。
$ mypy a4.py
a4.py:6: error: Argument 1 to "to_int" has incompatible type "float"; expected "Union[int, str]" [arg-type]
「floatは想定していない」と分かりました。小数を渡すと切り捨てられるため、意図しない結果になる箇所を事前に見つけられます。
Python 3.10以降は|が書ける
def to_int(v: int | str) -> int:
return int(v)
importが不要で、見た目も直感的です。3.10以降を対象にするなら、こちらが推奨されています。
Python 3.9で使うとエラーになります。
x = int | str
実行結果(Python 3.9):
TypeError: unsupported operand type(s) for |: 'type' and 'type'
| 対象 | 書き方 |
|---|---|
| 3.9以前も含む | Union[int, str] |
| 3.10以降のみ | int | str |
使う側では型を絞り込む
Unionを受け取った関数の中で、片方にしかないメソッドを呼ぶと怒られます。
def show(v: Union[int, str]) -> None:
print(v.upper()) # strにしかない
error: Item "int" of "Union[int, str]" has no attribute "upper"
isinstance()で分岐すれば、型チェッカーが各分岐の中での型を理解します。
def show(v: Union[int, str]) -> None:
if isinstance(v, str):
print(v.upper()) # ここではstr
else:
print(v + 1) # ここではint
これを型の絞り込み(narrowing)と呼びます。Unionを使うときは、ほぼ必ずisinstance()とセットになります。
Optionalとの関係
from typing import Optional, Union
print(Optional[int] == Union[int, None])
実行結果:
True
Optional[X]はUnion[X, None]の短縮形です。Noneとの組み合わせならOptionalのほうが意図が明確なので、そちらを使ってください。
詳しくは「Python Optionalとは?Noneを許容する型ヒントの書き方」で解説しています。
Unionを使いすぎない
便利な反面、設計の甘さを覆い隠す道具でもあります。
def process(data: Union[str, list, dict, int, None]) -> Union[str, list, None]:
...
これは「何が来るか分からない」と言っているのと同じで、型ヒントの意味がほとんどありません。使う側は毎回全パターンを調べる必要があります。
| Unionが適切 | 見直すべき |
|---|---|
int | str(数値の文字列表現を許す) |
4つ以上並ぶ |
str | Path(パスの受け取り) |
戻り値がUnion |
X | None(=Optional) |
分岐ごとに処理が全く違う |
特に戻り値のUnionは扱いにくいです。呼び出し側に分岐を強制するためで、関数を分けるほうが素直なことがほとんどです。
def get_text(uid: int) -> str: ...
def get_rows(uid: int) -> list[str]: ...
数値はUnionを書かなくてよいことがある
def area(w: float, h: float) -> float:
return w * h
print(area(3, 4))
実行結果:
12
intを渡していますが、型チェッカーは通します。floatと書けばintも受け付けるという特別扱いがあるためです。
Union[int, float]と書く必要はありません。floatだけで十分です。
Unionのまとめ
Union[X, Y]は「XかY」- 3.10以降は
X | Yと書ける(3.9ではTypeError) - 使う側では
isinstance()で絞り込む Optional[X]はUnion[X, None]の短縮形- 並べすぎると型ヒントの意味が消える。4つ以上は設計を見直す
- 戻り値のUnionは避け、関数を分ける
- 数値は
floatと書けばintも通る





