【Python】関数の引数の型に応じて戻り値の型を動的に変える型ヒントの書き方
2024-07-04
Pythonでもジェネリクスみたいに型を書きつつそれを動的に扱えないかな~と思ってガチャガチャやって辿り着いたものを共有
環境
- Python 3.11.9
Python 3.12以降でより良い書き方あるかは不明
例
from typing import TypeVar
T = TypeVar("T", int, int | None)
def init_list(initial_value: T, num: int) -> list[T]:
# initial_value: T = 0 のようにデフォルト値も入れられる
res: list[T] = [initial_value] * num
return res
init_list(0, 1) # 戻り値の型は`list[int]`になる
init_list(None, 1) # 戻り値の型は`list[int | None]`になる
そもそもTypeVar
とは何か、から説明します。
簡単に言うと「Any
に少し制約を加えられ、実際の値の型になってくれるもの」という感じです。
つまり、Any
はどんな値が渡ってこようともAny
型でしかありませんが、
TypeVar
は例えばint
型の値が渡ってきたらint
型になってくれます。
よくUnion
と一緒に名前が出されますが、
Union
は、AもBも同時に許容するTypeVar
はAかBを許容する
といった違いがあります。
実際に使ってみた例を以下に示します。
# 両方の型を混ぜて使える
x: list[int | None] = [None, 0, 1]
U = TypeVar("U", int, None)
def to_list(*values: U) -> list[U]:
return list(values)
y1 = to_list(None, 0, 1) # できない
y2 = to_list(None, None) # できる
y3 = to_list(0, 1) # できる
なんならy3 = to_list(0, 1)
のときy3
の型はint
に絞られます。
(この場合は後続の処理で「y3
にNone
が含まれていたら~」といった判定も不要になる)
ちなみに最初に挙げた例では、このTypeVar
とUnion
を組み合わせを実現しています。
意外とうまいことやってくれるみたい
スポンサーリンク