Pythonの型ヒントUnionとTypeVarの違いと使い方
PythonのUnionとTypeVarの違いについて解説します。
Pythonの型ヒントは、Pythonの静的型付けのサポートを強化するために導入されました。型ヒントを使用することで、関数やメソッドがどのような型の引数を期待し、どのような型の戻り値を返すかを明示的に示すことができます。
型ヒントには、Union
とTypeVar
という2つの重要な機能があり、それぞれ似たような使い方ができるため、基本的な使い方とそれぞれの違いを解説します。
なお、Pythonにおける型ヒントはあくまでアノテーションであり、コンパイル時や実行時に型チェックを行なってエラーを出してくれるようなものではありません。以下の解説における『エラー』とは、Pylanceなどの静的型付けチェックツールにおけるエディタ上のエラーを指します。
Unionの基本的な使い方
まず、Union
は、指定された変数の型が、Union
内で指定した複数の型のうちの1つであることを示します。例えば、次のような関数があったとします。
def get_length(x):
return len(x)
この関数は、引数x
の長さを返す関数ですが、x
が文字列またはリストの場合にだけ機能し、たとえばint
型などではエラーになります。そこで、引数x
の型をUnion
を使って次のように定義することができます。
from typing import Union
# 型ヒントで、引数はstr型かlist型、戻り値はint型であることを示す
def get_length(x: Union[str, list]) -> int:
return len(x)
ここで、Union[str, list]
は、x
がstr
またはlist
のいずれかであることを示しています。このように、Union
を使用することで、関数やメソッドが受け入れることができる複数の型を指定することができます。
TypeVarの基本的な使い方
次に、TypeVar
は、ジェネリック関数やクラスで使用される型パラメーターを定義するために使用されます。例えば、次のようなジェネリック関数があったとします。
from typing import TypeVar, List
T = TypeVar('T')
# List型の変数を引数にとり、その要素の型を戻り値の型に指定している
def get_first_element(x: List[T]) -> T:
return x[0]
この関数は、リストの最初の要素を返す関数ですが、リストの要素の型が何であるかは、引数に実際のリストが渡されるまでわかりません。そのため、リストの要素の型を取り出して、それを戻り値として使うことを示すためにTypeVar
を使用しています。
また、TypeVarは指定できる型を制限することもできます。たとえば、T = TypeVar('T', int, str)
のような書き方で、T
に指定する型をint
型とstr
型に制限できます。
from typing import TypeVar
T = TypeVar('T', int, str)
# int型かstr型の引数と戻り値を指定
def multiply_by_two(x: T) -> T:
return x * 2
UnionとTypeVarの違い
上記の例を見ると、Union
は、複数の型を組み合わせた場合に使用され、TypeVar
は、型パラメーターを定義する場合に使用されることがわかります。
両方とも、引数や戻り値の型を、複数の種類に制限することができますが、Union
はあくまで型の集合、TypeVar
は複数のパターンがある型変数であるという点で異なります。
たとえば、Union[int, str]
はint
型かstr
型を許容する複合的な型の集合であり、たとえ指定した引数や戻り値がどちらの型であるかがわかっているとしても、型チェックの時点では複合的な型であると判断されます。そのため、以下のような例では、戻り値でint
型のメソッドを利用することができません。
from typing import Union
def multiply_by_two(x: Union[int, str]) -> [int, str]:
return x * 2
num: int = multiply_by_two(10) # Pylanceなどでエラーになる
num: Union[int, str] = multiply_by_two(10) # この書き方ならOKだが、int型ではなくなる
num.to_bytes(2, byteorder='big') # そのため、int型のメソッドを扱うとPylanceなどでエラーが出る
一方、TypeVar('T', int, str)
は、int
型かstr
型を許容する型変数T
を宣言しており、関数の使用時に型が確定したのであれば、その引数や戻り値はその確定した型として扱うことができます。
from typing import TypeVar
T = TypeVar('T', int, str)
def multiply_by_two(x: T) -> T:
return x * 2
num: int = multiply_by_two(10) # エラーは出ず、int型として扱うことができる
num.to_bytes(2, byteorder='big') # int型のメソッドも問題なく扱うことができる