Home > Software design >  How can I define a TypeAlias for a nested Generic in Python?
How can I define a TypeAlias for a nested Generic in Python?

Time:01-09

I currently have this code

T = TypeVar("T")
Grid = Sequence[Sequence[T]]

def columns(grid: Grid) -> Iterable[list[T]]:
    return ([row[i] for row in grid] for i in range(len(grid[0])))

But I think the T in the alias Grid is bound to a different T in the return type of the function.

How do I define Grid such that I can write

def columns(grid: Grid[T]) -> Iterable[list[T]]:
    ...

I've looked at typing.GenericAlias, but can't see how it helps me.

(I'm aware that Sequence[Sequence[T]] has no guarantee that the grid is actually rectangular, but that's not the problem I want to focus on here.)

CodePudding user response:

When using type variable as a generic parameter, it can be replaced by other type variables, which is mentioned in the Generic Alias Type (but I only found this one):

The __getitem__() method of generic containers will raise an exception to disallow mistakes like dict[str][str]:

>>> dict[str][str]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: There are no type variables left in dict[str]

However, such expressions are valid when type variables are used. The index must have as many elements as there are type variable items in the GenericAlias object’s __args__.

>>> from typing import TypeVar
>>> Y = TypeVar('Y')
>>> dict[str, Y][int]
dict[str, int]

So there is no problem with your current implementation. In the interactive interpreter, you will see:

>>> from collections.abc import Sequence
>>> from typing import TypeVar
>>> T, R = TypeVar('T'), TypeVar('R')
>>> Grid = Sequence[Sequence[T]]
>>> Grid
collections.abc.Sequence[collections.abc.Sequence[~T]]
>>> Grid[R]
collections.abc.Sequence[collections.abc.Sequence[~R]]

Mypy will also correctly analyze them:

from collections.abc import Sequence, Iterable
from typing import TypeVar

T = TypeVar('T')
Grid = Sequence[Sequence[T]]


def columns(grid: Grid[T]) -> Iterable[list[T]]:
    return ([row[i] for row in grid] for i in range(len(grid[0])))


c1: Iterable[list[int]] = columns([[1, 2, 3]])  # pass
c2: Iterable[list[int]] = columns([[4, 5, '6']])
# List item 2 has incompatible type "str"; expected "int" (13:42)
  • Related