Данный аргумент списка[int] | list[str], могу ли я быть уверен, что список — это list[int], если элемент [0] — int, и наоборот?

У меня есть сценарий python, и я пытаюсь добавить в код подсказки типов. Ниже приведен пример кода (без подсказок типа код работает) с использованием mypy.

values_int: list[int] = [1, 2, 3, 4]
values_str: list[str] = ["1", "2", "3", "4"]

def bar(*, x: list[int]) -> bool:
    # processing
    return True

def baz(*, y: list[str]) -> bool:
    # processing
    return True

def foo(*, values: list[int] | list[str]) -> bool:
    status: bool = False

    if isinstance(values[0], int):
        x: list[int] = values
        status = bar(x=x)

    elif isinstance(values[0], str):
        # case-1
        # status = baz(y=values)

        # case-2
        y: list[str] = values
        status = baz(y=y)

    return status

foo(values=values_str)

Ошибки для:

# case-1
#  error: Argument "y" to "baz" has incompatible type "list[int] | list[str]"; expected "list[str]"

# case-2
#  error: Incompatible types in assignment (expression has type "list[int] | list[str]", variable has type "list[str]")

@tripleee, список всегда имеет один тип данных: либо все int, либо все str

winter 04.06.2024 11:11

Я понимаю, что вы хотите, чтобы это значило именно это, но mypy хочет гарантий. (Думаю. В конце концов я удалил свой комментарий, по крайней мере, до тех пор, пока не смогу разобраться в этом самостоятельно.)

tripleee 04.06.2024 11:13

Подсказки по типам Синтаксис параметров типа PEP 695 поддерживаются в Python 3.12, но (пока) не поддерживаются в mypy.

SIGHUP 04.06.2024 13:00
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
3
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

isinstance(a[b], ...)(пока) не поддерживается как конструкция сужения типа. Как бы то ни было, Pyright также его не поддерживает.

Возможно, вам нужен индивидуальный TypeIs:

(playground links: Mypy , Pyright)

from typing_extensions import TypeIs

def is_list_of_ints(v: list[Any]) -> TypeIs[list[int]]:
  return isinstance(v[0], int)

def is_list_of_strs(v: list[Any]) -> TypeIs[list[str]]:
  return isinstance(v[0], int)
def foo(*, values: list[int] | list[str]) -> None:
    reveal_type(values)      # list[int] | list[str]
    
    if is_list_of_ints(values):
        reveal_type(values)  # list[int]
        bar(x=values)        # fine

    elif is_list_of_strs(values):
        reveal_type(values)  # list[str]
        baz(y=values)        # fine

Обратите внимание, что в Mypy 1.10.0 есть ошибка с TypeIs: она неправильно определяет вторую условную ветвь как недоступную:

def foo(*, values: list[int] | list[str]) -> None:
    reveal_type(values)      # list[int] | list[str]
    
    if is_list_of_ints(values):
        reveal_type(values)  # list[int]
        bar(x=values)        # fine

    elif is_list_of_strs(values):
        reveal_type(values)  # error: Statement is unreachable
        baz(y=values)

Обходной путь — использовать TypeGuard:

(playground link)

from typing import TypeGuard

def is_list_of_ints(v: list[Any]) -> TypeGuard[list[int]]:
  return isinstance(v[0], int)

def is_list_of_strs(v: list[Any]) -> TypeGuard[list[str]]:
  return isinstance(v[0], int)

Разница между TypeIs и TypeGuard объясняется в PEP 742.

Другие вопросы по теме