Как аннотировать функцию, которая принимает кортеж переменной длины? (аннотация типа вариативного кортежа)

У меня есть функция, которая принимает в качестве аргумента кортеж разной длины:

from typing import Tuple


def process_tuple(t: Tuple[str]):
    # Do nasty tuple stuff

process_tuple(("a",))
process_tuple(("a", "b"))
process_tuple(("a", "b", "c"))

Когда я аннотирую функцию, как упомянуто выше, я получаю эти сообщения об ошибках

fool.py:9: error: Argument 1 to "process_tuple" has incompatible type "Tuple[str, str]"; expected "Tuple[str]"
fool.py:10: error: Argument 1 to "process_tuple" has incompatible type "Tuple[str, str, str]"; expected "Tuple[str]"

process_tuple действительно работает с кортежами, и я использую их как неизменяемые списки переменной длины. Я не нашел консенсуса по этой теме в Интернете, поэтому мне интересно, как мне аннотировать такой ввод.


person Montreal    schedule 18.02.2019    source источник
comment
Вы получаете эти ошибки при запуске кода? Я запустил код без ошибок.   -  person fountainhead    schedule 18.02.2019
comment
Я получаю эти ошибки при запуске mypy.   -  person Montreal    schedule 18.02.2019


Ответы (2)


Однородный кортеж переменной длины, который мы можем аннотировать, используя литерал ... (он же Ellipsis), например

def process_tuple(t: Tuple[str, ...]):
    ...

после этого ошибки должны исчезнуть.

Из документов

Чтобы указать кортеж переменной длины однородного типа, используйте буквальное многоточие, например Tuple[int, ...]. Обычный Tuple эквивалентен Tuple[Any, ...] и, в свою очередь, tuple.

person Azat Ibrakov    schedule 18.02.2019
comment
На самом деле это немного нелогично и нелогично. Если мы предположим, что List[str] подходит для списков переменной длины, то почему Tuple[str] не подходит для кортежей переменной длины? И (type(("a", "a")) == type(("a", "a", "a")) дает True. - person Montreal; 19.02.2019
comment
@Montreal: это потому, что tuples и lists служат для разных целей: tuples - это разнородные контейнеры (например, позиционные аргументы для произвольной функции или отдельной записи таблицы из RDBMS, или в мире математики - элементы декартова произведения различных наборов (или объединение декартовых произведений), поэтому каждая координата может иметь разный тип, но их количество обычно фиксировано), а lists однородны (например, набор одинаковых записей таблицы или конечная последовательность элементов некоторого набора) - person Azat Ibrakov; 19.02.2019
comment
@Montreal: так Tuple[str] - это единственный str-объект tuple, а List[str] - это набор произвольного количества str объектов - person Azat Ibrakov; 19.02.2019

В дополнение к ответу с многоточием, опубликованному Азатом, вы можете сделать его более явным, используя @typing.overload или typing.Union

from typing import Tuple


@overload
def process_tuple(t: Tuple[str]):
    # Do nasty tuple stuff

@overload
def process_tuple(t: Tuple[str, str]):
    ...

Или с Союзом:

from typing import Tuple, Union


def process_tuple(t: Union[Tuple[str], Tuple[str, str], Tuple[str, str, str]]):
    # Do nasty tuple stuff
person Wolph    schedule 18.02.2019
comment
Я знал об этом, но мои кортежи могут быть очень длинными, так что это не вариант. В любом случае, благодарю Вас. - person Montreal; 18.02.2019
comment
btw overloads должен пройти до реализации - person Azat Ibrakov; 18.02.2019