361

How do I use type hints to annotate a function that returns an Iterable that always yields two values: a bool and a str? The hint Tuple[bool, str] is close, except that it limits the return value type to a tuple, not a generator or other type of iterable.

I'm mostly curious because I would like to annotate a function foo() that is used to return multiple values like this:

always_a_bool, always_a_str = foo()

Usually functions like foo() do something like return a, b (which returns a tuple), but I would like the type hint to be flexible enough to replace the returned tuple with a generator or list or something else.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Richard Hansen
  • 51,690
  • 20
  • 90
  • 97
  • Possible duplicate of [How to specify multiple return types using type-hints](https://stackoverflow.com/questions/33945261/how-to-specify-multiple-return-types-using-type-hints) – Stevoisiak Feb 09 '18 at 14:44
  • 12
    @StevenM.Vascellaro This is not a duplicate of that question – T.W.R. Cole Mar 26 '19 at 18:39
  • Related: https://stackoverflow.com/questions/58101021/python-type-hints-for-function-returning-multiple-return-values – Nav Feb 05 '23 at 06:42

1 Answers1

496

You are always returning one object; using return one, two simply returns a tuple.

So yes, -> Tuple[bool, str] is entirely correct.

Only the Tuple type lets you specify a fixed number of elements, each with a distinct type. You really should be returning a tuple, always, if your function produces a fixed number of return values, especially when those values are specific, distinct types.

Other sequence types are expected to have one type specification for a variable number of elements, so typing.Sequence is not suitable here. Also see What's the difference between lists and tuples?

Tuples are heterogeneous data structures (i.e., their entries have different meanings), while lists are homogeneous sequences. Tuples have structure, lists have order.

Python's type hint system adheres to that philosophy, there is currently no syntax to specify an iterable of fixed length and containing specific types at specific positions.

If you must specify that any iterable will do, then the best you can do is:

-> Iterable[Union[bool, str]]

at which point the caller can expect booleans and strings in any order, and of unknown length (anywhere between 0 and infinity).

Last but not least, as of Python 3.9, you can use

-> tuple[bool, str]

instead of -> Tuple[bool, str]; support for type hinting notation has been added to most standard-library container types (see PEP 585 for the complete list). In fact, you can use this as of Python 3.7 too provided you use the from __future__ import annotations compiler switch for your modules and a type checker that supports the syntax.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 5
    The [language specification](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements) allows returning other iterables; `foo()` could do `yield True; yield "blah"` and `a, b = foo()` would still work. Or `foo()` could return a list. I reworded my question to make it clear that I'm interested in hinting an arbitrary iterable, not a tuple. – Richard Hansen Oct 21 '16 at 16:46
  • 2
    @RichardHansen: that may well be, but type hinting only provides you with `Tuple` to express a heterogenous, fixed length return value. – Martijn Pieters Oct 21 '16 at 16:51
  • @RichardHansen: if you want to return a list, then that's fine, but there is no facility to express that the list will have a fixed length and that specific elements in that list have specific values. – Martijn Pieters Oct 21 '16 at 16:52
  • 4
    "What you want is not possible" is a good answer, assuming it's correct. :) – Richard Hansen Oct 21 '16 at 17:02
  • 4
    @RichardHansen: I've re-checked the PEPs and documentation of `typing` and `mypy` another 2 times since posting; I'm pretty confident I didn't miss anything. That said, there are several regulars here on SO with plenty of Python type hinting experience that won't hesitate to correct me if turn out to be wrong, or will post a better answer. – Martijn Pieters Oct 21 '16 at 17:06
  • 4
    In order to run this code in python 3.8 you may have to add `from typing import Tuple, List` – Joel Sep 08 '21 at 07:30
  • 1
    @Joel: that applies to any Python 3 version; the `from typing import ...` is implied. – Martijn Pieters Sep 08 '21 at 10:31
  • And how about naming the returned tuple values in order to document them? Many functions do return or yield multiple values and we need to also document their meaning. Can can we put a name for each of them? – sorin Jan 22 '22 at 09:04
  • @sorin use a [NamedTuple](https://stackoverflow.com/questions/2970608/what-are-named-tuples-in-python). – Martijn Pieters Jan 22 '22 at 09:43
  • 1
    I wonder if there is also syntax for type hinting the values in the caller site, i.e. type hinting _a_ and _b_ in: `a, b = fn()` – matanster Jan 25 '22 at 20:07
  • 1
    @matanster: not when assigning to multiple variables; you can annotate when there is a single variable: `res: tuple[typea, typeb] = fn()`. Unpack on a separate line: `a, b = res`. Or create separate type declarations first. – Martijn Pieters Jan 26 '22 at 00:32
  • 1
    @Martijn Pieters why is `from typing` implied? since we can use `getitem` on built-in types, it seems like it's not necessary anymore (e.g. `tuple[this, that]`). is this wrong? maybe it's still required in some instances? – william_grisaitis May 25 '22 at 17:44
  • 1
    @william_grisaitis: no, if you are not using the `typing.Tuple` object then you don't need to import it. – Martijn Pieters May 27 '22 at 11:29
  • How to do add types to the variables for example: x,y,x =foo() – user1689987 Apr 30 '23 at 18:54
  • @user1689987: if `foo()` has proper type hints about what it returns, you don't need to add types to the variables. Otherwise, you can put annotations before that line (`x: int`, newline, `y: str`, newline, `z: bool`, newline), or assign to a single variable `xyz: tuple[int, str, bool] = foo()` and then unpack the single variable. – Martijn Pieters Jun 09 '23 at 10:10