Python 3.10では PEP 647: ユーザ定義の型ガード が導入され、型ガード を定義して型推論に利用できるようになりました。
from typing import TypeGuard
def is_int(x: object) -> TypeGuard[int]:
return isinstance(x, int)
def func(x:object) -> None:
if is_int(x):
reveal_type(x) # x は int
型ガードについては、Python 3.10の新機能(その7) ユーザ定義型ガード の解説を参照してください。
型ガードの問題点¶
型ガード関数は自由度が高く、指定した引数とは無関係な型を指定したり、第一引数のオブジェクト以外の引数を使って型を判定することを認められています。このために発生する不整合を避けるために、以下のルールが定められています。
型ガードが
True
を返す場合、その引数の型は常に型ガードで指定した以外の情報を使って推論してはならない。次の例では、ガードしたブロック内ではy
は常にX
としてのみ扱われる。def is_x(y: Y) -> TypeGuard[X]: return True y:Y = Y() if is_x(y): reveal_type(y) # y は X
型ガードが
False
を返す場合、その情報を推論に使ってはならない。def is_black_cat(cat: Cat, color: Color) -> TypeGuard[Cat]: return color == Color.Black cat: Cat = Cat() if not is_black_cat(cat): reveal_type(cat) # catはCat。 is_black_cat()の結果を推論に利用してはならない
しかし、型ガードのリリース後、このようなルールでは不便なケースがあることが明らかになってきました。例えば、次のような場合です。
def is_list(o:object)->TypeGuard[list[Any]]:
return isinstance(o, list)
a:list[int] = [1]
if is_list(a):
reveal_type(a) # a は list[Any]
この場合、is_list()
の値が True
なら a
は list[int]
であることは明らかですが、前述のルールのため、list[Any]
と推論しなければなりません。
また、次のケースを考えてみましょう。
def is_str(o:int|str)->TypeGuard[str]:
return isinstance(o, str)
def foo(a:int|str)->None:
if is_str(a):
reveal_type(a) # a は str
else:
reveal_type(a) # a は int | str
この場合、is_str(a)
の結果が False
なら、a
が int
であることは明らかです。しかし、is_str(a)
が False
の場合に、その結果を使って a
の型を int
と推論することは認められていません。
PEP 742: Narrowing types with TypeIs¶
PEP 742では、このような用途に対応するために、新しく 型の絞り込み を行う TypeIs
という形式が導入されました。
from typing import TypeIs
def is_str(o:int|str)->TypeIs[str]:
return isinstance(o, str)
TypeIs
は TypeGuard
と似ていますが、TypeIs
で絞り込む型は、引数の型に互換性を持たなければなりません。また、複数のパラメータを指定する場合、2番目以降の引数を絞り込みに利用してはなりません。
TypeIs
では、この条件によって TypeGuard
にあった制限を緩和し、次のような絞り込みを行えるようになっています。
def is_list(o:object)->TypeIs[list[Any]]:
return isinstance(o, list)
a:list[int] = [1]
if is_list(a):
reveal_type(a) # a は list[int] と推論可能
def is_str(o:int|str)->TypeGuard[str]:
return isinstance(o, str)
def foo(a:int|str):
if is_str(a):
reveal_type(a) # a は str
else:
reveal_type(a) # a は int と推論可能
このような絞り込みは、型述語(type predicate) とも呼ばれます。