LoginSignup
5

More than 1 year has passed since last update.

PEP 647 (User-Defined Type Guards) を読んだよメモ

Posted at

今朝、PEP 647 (User-Defined Type Guards)Accepted になったというPRを見かけました。
そこで、今回は PEP 647 を読んでみようと思います。

概要

  • 型チェッカーツールでは type narrowing と呼ばれる手法を使って、プログラム内で型情報をより正確に決定しています。

以下の例では if文と is None を利用して、自動的に if 文の中の型が絞り込まれます。

  def func(val: Optional[str]):
    # "is None" type guard
    if val is not None:
        # Type of val is narrowed to str
        ...
    else:
        # Type of val is narrowed to None
        ...

他にも isinstance() など、いくつかの判定で type narrowing が行われています。

  • しかし、以下のようにユーザー関数を利用して判定している場合には、type narrowing は意図通りには働きません。
  def is_str_list(val: List[object]) -> bool:
    """Determines whether all objects in the list are strings"""
    return all(isinstance(x, str) for x in val)

  def func1(val: List[object]):
    if is_str_list(val):
        print(" ".join(val)) # Error: invalid type
  • そこで、新しい型情報 typing.TypeGuard を通じて ユーザー定義 型ガード (user-defined type guard) を定義できるようにします
  • ユーザー定義 型ガードを用いることで、type narrowing のサポートを受けやすくなります
  • ユーザー定義 型ガードは pyright にはすでに実装済みで、 typing.TypeGuard による定義は 3.10 から利用可能です

アプローチ

is_str_list() のような bool を返す関数の返り値の型として typing.TypeGuard を指定します。

from typing import TypeGuard

def is_str_list(val: List[object]) -> TypeGuard[List[str]]:
    """Determines whether all objects in the list are strings"""
    return all(isinstance(x, str) for x in val)

型チェッカーは、この関数が True を返した場合に、先頭の引数を TypeGuard に指定した型に合致するとみなします。上記の例では、 is_str_list() をパスしたデータは List[str] として扱われます。

なお、この関数が False を返した場合は type narrowing は行われません。
以下の例では、 if is_two_element_tuple(...) のブロックでは type narrowing が行われた結果、型が Tuple[str, str] に絞り込まれるのに対して、else ブロックでは型は変化しません。

def is_two_element_tuple(val: Tuple[str, ...]) -> TypeGuard[Tuple[str, str]]:
    return len(val) == 2

OneOrTwoStrs = Union[Tuple[str], Tuple[str, str]]
def func(val: OneOrTwoStrs):
    if is_two_element_tuple(val):
        reveal_type(val)  # Tuple[str, str]
        ...
    else:
        reveal_type(val)   # OneOrTwoStrs

Union を使っているので、 Tuple[str] に絞り込まれるような印象を持ちますが、ユーザー定義 型ガードではそのようには動かないので注意が必要です。

感想

  • 条件文で型が絞り込まれていることは知っていたけど、type narrowing と呼ばれているのは知らなかった
  • 致し方なく type: ignore している箇所が減らせるのは気持ちよさそう
  • 細かすぎる機能な気がするものの、かゆいところに手が届く感じがある

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5