Сообщить об ошибке.

Конструкция типизации TypeIs[] модуля typing в Python

Конструкция типизации для обозначения функций-предикатов пользовательского типа

Синтаксис:

# Добавлено в Python 3.13.
from typing import TypeIs

class Parent: pass
class Child(Parent): pass

def is_parent(val: object) -> TypeIs[Parent]:
    return isinstance(val, Parent)

def run(arg: Child | Unrelated):
    # Тип `arg` сужается до пересечения
    # из `Parent` и `Child`, что эквивалентно `Child`
    if is_parent(arg):
        ...

Назначение:

  • сужение типов

Описание typing.TypeIs[]:

Добавлено в Python 3.13.

Специальная конструкция типизации TypeIs[] модуля typing предназначена для маркировки функций предиката определяемого пользователем типа.

typing.TypeIs[] можно использовать для аннотирования типа возвращаемого значения функции-предиката определяемого пользователем типа. Конструкция typing.TypeIs принимает только один аргумент типа. Во время выполнения функции, помеченные таким образом, должны возвращать логическое значение и принимать хотя бы один позиционный аргумент.

Цель TypeIs[] - сужение типов - метод, используемый средствами проверки статических типов для определения более точного типа выражения в потоке кода программы. Обычно сужение типа выполняется путем анализа потока условного кода и применения сужения к блоку кода. Условное выражение здесь иногда называют "предикатом типа":

def is_str(val: str | float):
    # Предикат типа 'isinstance'
    if isinstance(val, str):
        # Тип `val` сужается до `str`
        ...
    else:
        # В противном случае тип `val` сужается до `float`.
        ...

Иногда было бы удобно использовать определяемую пользователем логическую функцию в качестве предиката типа. Такая функция должна использовать TypeIs[...] или TypeGuard в качестве возвращаемого типа, чтобы предупредить об этом средства проверки статических типов. TypeIs обычно имеет более интуитивное поведение, чем typing.TypeGuard, но его нельзя использовать, когда типы ввода и вывода несовместимы (например, list[object] со list[int]) или когда функция не возвращает True для всех экземпляров суженного типа.

Использование -> TypeIs[NarrowedType] сообщает средству проверки статического типа, что для данной функции:

  • Возвращаемое значение является логическим.
  • Если возвращаемое значение равно True, то тип его аргумента является пересечением исходного типа аргумента и NarrowedType.
  • Если возвращаемое значение равно False, то тип его аргумента сужается, чтобы исключить NarrowedType.
from typing import assert_type, final, TypeIs

class Parent: pass
class Child(Parent): pass

@final
class Unrelated: pass

def is_parent(val: object) -> TypeIs[Parent]:
    return isinstance(val, Parent)

def run(arg: Child | Unrelated):
    if is_parent(arg):
        # Тип `arg` сужается до пересечения
        # из `Parent` и `Child`, что эквивалентно `Child`
        assert_type(arg, Child)
    else:
        # Тип `rg`` сужен, для исключения `Parent`,
        # так что остается только `Unrelated`.
        assert_type(arg, Unrelated)

Тип внутри typing.TypeIs должен соответствовать типу аргумента функции. Если это не так, то средства проверки статического типа выдадут ошибку. Неправильно написанная функция typing.TypeIs может привести к некорректному поведению системы типов. Ответственность за написание таких функций типобезопасным образом лежит на пользователе.

Если конструкция typing.TypeIs является методом класса или экземпляра, то тип в TypeIs сопоставляется с типом второго аргумента (после cls или self).

Короче говоря, форма def foo(arg: TypeA) -> TypeIs[TypeB]: ... означает, что если foo(arg) возвращает True, то arg является экземпляром TypeB, а если он возвращает False, то это не экземпляр TypeB.