'How to type a function attribute in NamedTuple

I have a piece of code like

from typing import Callable, NamedTuple, TypeVar


def f1(x: int) -> int:
    return x


def f2(y: str) -> int:
    return len(y)


T = TypeVar("T", int, str)


class Config(NamedTuple):
    func: Callable[[T], int]


c1 = Config(func=f1)
c2 = Config(func=f2)

Mypy complains:

toy.py:19:18: error: Argument "func" to "Config" has incompatible type "Callable[[int], int]"; expected "Callable[[Config], int]"
toy.py:20:18: error: Argument "func" to "Config" has incompatible type "Callable[[str], int]"; expected "Callable[[Config], int]"
Found 2 errors in 1 file (checked 1 source file)

Why would it expect "Callable[[Config], int]"?

I'm using Python-3.8, and mypy==0.800



Solution 1:[1]

If you want the type of that field to be either a function of an int or a function of a str, you can do

class Config(NamedTuple):
    func: Callable[[int], int] | Callable[[str], int]

This describes the union of two function types.

However, this is not a useful type. If Python had intersection types, this would be equivalent to

Callable[[int & str], int]

And int & str is, well, the empty type. There are no values which are both int and str at the same time. So this is a type of functions that can never be called. And isinstance won't help you here since you can't typecheck the declared argument types of a Callable.

As mentioned in the comments, you can do

Callable[[int | str], int]

but this is the type of functions that accept either an int or a string, not the type of functions that accept only one of the two selectively.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Silvio Mayolo