pythonpython-typingbuilt-in

Finding the proper Python type hint, for instance, the signature of the built-in function map()


Description

In Python 3.5 or above, type hints are supported (see here for more information). However, the proper usage for common types are not well documented.

For instance, from the official site, I could gather the following (proper) usages:

String

# The function signature of greeting is: Callable[[str], str]
def greeting(name: str) -> str:
    return 'Hello ' + name

Integer

# The function signature of add is: Callable[[int, int], int]
def add(a: int, b: int) -> int:
    return a + b

Floating Point Number

# The default value of x is 1.0
def reciprocal(x: float = 1.0) -> float:
    from math import nan
    if x == 0:
        return nan
    return 1 / x

List

from typing import List, TypeVar

T = TypeVar('T')

def repeat(x: T, n: int) -> List[T]:
    return [x] * n

Tuple

from typing import Tuple, TypeVar

T = TypeVar('T')

def double(x: T) -> Tuple[T, T]:
    return (x, x)

Questions

My questions are:

1. What is the return type of map?

from typing import Iterable

# Is this correct?
x: Iterable[float] = map(int.__float__, [1, 2, 3])

I am not sure if this is the correct type hint for x above.

2. In a broader sense, what is the 'function signature' of map?

from typing import Callable, Iterable, TypeVar

T = TypeVar('T')
U = TypeVar('U')

# In the above usage, the type of the map function seems to be:
Function1 = Callable[[T], U]
typeOfMap = Callable[[Function1, Iterable[T]], Iterable[U]]

# Or in one line:
typeOfMap = Callable[[Callable[[T], U], Iterable[T]], Iterable[U]]

But in fact, the map function can accept multiple iterables. It is documented as:

map(function, iterable, ...)

It could be used like this:

# The result is: [['A'], ['B', 'B'], ['C', 'C', 'C']]
result = list(map(repeat, ['A', 'B', 'C'], [1, 2, 3]))

T1 = TypeVar('T1')
T2 = TypeVar('T2')
R = TypeVar('R')

# So, the type of map function in this usage seems to be:
Function2 = Callable[[T1, T2], R]
typeOfMap = Callable[[Function2, Iterable[T1], Iterable[T2]], Iterable[R]]

In general, I guess it should be something like below, but this is not the correct way to write it:

FunctionN = Callable[[T1, T2, ..., Tn], R]
typeOfMap = Callable[[FunctionN, Iterable[T1], Iterable[T2], ..., Iterable[Tn]], Iterable[R]]

So, what is the correct way to write it?

3. In general, where can I find the correct type hint of a Python function / method, including the built-in ones, those in the core libraries?

I need them mainly for learning purpose.

4. Is there any way to output the type inference result computed by the compiler / interpreter, as in Haskell or Erlang?

I know Haskell and Erlang are functional programming languages and variables are immutable, so it would be much easier to make this possible, but just in case if Python also has similar functionalities, I would like to know.

5. Is there any way to check that my type hint is correct?

Or at least let it show me some warning / error at compile time / runtime so that I am aware that something is wrong.

References

At the time of this writing, the latest stable version is 3.8.0.


Solution

  • 1. What is the return type of map?

    map’s return value is currently typed as an Iterator[T]. Since iterators are iterables, your annotation of x: Iterable[float] = map(int.__float__, [1, 2, 3]) is valid. (Also, just write map(float, [1, 2, 3]).)

    2. In a broader sense, what is the 'function signature' of map?

    It’s currently impossible to represent, which is why typeshed has overloads for up to 6 generic parameters. (one related issue)

    3. In general, where can I find the correct type hint of a Python function / method, including the built-in ones, those in the core libraries?

    I need them mainly for learning purpose.

    Python’s typeshed repository. Note that for non-learning purposes – practice – you should generally either let the type be inferred or use whatever type makes sense rather than the most exact type possible.

    You can also use reveal_type – see below.

    4. Is there any way to output the type inference result computed by the compiler / interpreter, as in Haskell or Erlang?

    I know Haskell and Erlang are functional programming languages and variables are immutable, so it would be much easier to make this possible, but just in case if Python also has similar functionalities, I would like to know.

    This is up to your typechecker. Mypy has reveal_type.

    x = map(float, [1, 2, 3])
    reveal_type(x)  # note: Revealed type is 'typing.Iterator[builtins.float*]'
    

    5. Is there any way to check that my type hint is correct?

    Or at least let it show me some warning / error at compile time / runtime so that I am aware that something is wrong.

    Statically, that’s what typechecking is. (For example, Mypy would have told you if your x hint were wrong.) It can’t catch everything, especially in a language as dynamic as Python, but that’s the nature of programming.

    There are a few projects that do some level of runtime assertions based on type annotations, but none I’ve used or would really bother with.