How to annotate function that takes a tuple of variable length? (variadic tuple type annotation)

21,099

Solution 1

We can annotate variable-length homogeneous tuples using the ... literal (aka Ellipsis) like this:

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

or for Python3.9+

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

After that, the errors should go away.

From the docs:

To specify a variable-length tuple of homogeneous type, use literal ellipsis, e.g. Tuple[int, ...]. A plain Tuple is equivalent to Tuple[Any, ...], and in turn to tuple.

Solution 2

In addition to the Ellipsis answer as posted by Azat you could make it more explicit by using @typing.overload or 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]):
    ...

Or with the Union:

from typing import Tuple, Union


def process_tuple(t: Union[Tuple[str], Tuple[str, str], Tuple[str, str, str]]):
    # Do nasty tuple stuff
Share:
21,099
Montreal
Author by

Montreal

If you think hard enough you can do the impossible.

Updated on October 07, 2021

Comments

  • Montreal
    Montreal over 2 years

    I have a function that takes a tuple of different lengths as an argument:

    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"))
    
    

    When I annotate function like mentioned above, I get these error messages

    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 really works with tuples and I use them as immutable lists of variable length. I haven't found any consensus on this topic on the internet, so I wonder how should I annotate this kind of input.

  • Montreal
    Montreal over 5 years
    I knew about it, but my tuples can be very lengthy, so it is not an option. Anyway, thank you.
  • Azat Ibrakov
    Azat Ibrakov over 5 years
    btw overloads should go before implementation
  • Montreal
    Montreal over 5 years
    This actually is a little bit counter-intuitive and counter-logical. If we assume that List[str] is okay for variable length lists, then why Tuple[str] is not okay for variable length tuple? And (type(("a", "a")) == type(("a", "a", "a")) yields True.
  • Azat Ibrakov
    Azat Ibrakov over 5 years
    @Montreal: that's because tuples and lists serve for different purposes: tuples are heterogeneous containers (like positional arguments to an arbitrary function or a single table record from RDBMS, or in math world -- elements of cartesian product of different sets (or a union of cartesian products), so each coordinate may have a different type, but their number is usually fixed), while lists are homogeneous (like collection of same table records or a finite sequence of elements of some set)
  • Azat Ibrakov
    Azat Ibrakov over 5 years
    @Montreal: so Tuple[str] is a single-str-object tuple, while List[str] is a collection of arbitrary number of str objects