I'd like to define a generic type. Something like:
from typing import TypeVar, Sequence, Union, Generic
T = TypeVar('T')
RecurSeqOf = Sequence[Union[Generic[T], Sequence[T]]]
# mypy error: Variable "typing.Generic" is not valid as a type
Is there a way to do it?
The whole background
Actually, I need a recursive generic type like
T = TypeVar('T')
RecurSeqOf = Sequence[Union[T, 'RecurSeqOf']]]
But the definition of recursive types is not yet supported by mypy.
That's why I work around this issue by making nested type definitions up to a limited depth (say, usually 5-6 levels, but in the example below two levels only for the sake of conciseness). Hence, all the more the need to shorten the pattern because I need to use it for different parameter types:
from typing import Sequence, Union, TypeVar, Generic
class A:
pass
class B:
pass
# RecurSeqOfA = Sequence[Union[A, 'RecurSeqOfA']] # mypy error: Cannot resolve name "RecurSeqOfA" (possible cyclic definition)
RecurSeqOfA = Sequence[Union[A, Sequence[Union[A, Sequence[A]]]]]
# RecurSeqOfA = Sequence[Union[A, 'RecurSeqOfA']] # mypy error: Cannot resolve name "RecurSeqOfA" (possible cyclic definition)
RecurSeqOfB = Sequence[Union[B, Sequence[Union[B, Sequence[B]]]]]
T = TypeVar('T')
# RecurSeqOf = Sequence[Union[Generic[T], 'RecurSeqOf']] # error: Cannot resolve name "RecurSeqOf" (possible cyclic definition)
# additionally: error: Variable "typing.Generic" is not valid as a type
RecurSeqOf = Sequence[Union[Generic[T], Sequence[Generic[T]]]] # error: Variable "typing.Generic" is not valid as a type
As suggested by the comment of MisterMiyagi:
from typing import TypeVar, MutableSequence
T = TypeVar('T', bound='RecurSeqOf')
RecurSeqOf = MutableSequence[T]
a: RecurSeqOf[str] = []
a.append("abc")
a.append([]) # mypy error: error: Argument 1 to "append" of "MutableSequence" has incompatible type "List[<nothing>]"; expected "str"
b: RecurSeqOf[str] = []
a.append(b) # mypy error: Argument 1 to "append" of "MutableSequence" has incompatible type "MutableSequence[str]"; expected "str"
a.append(["cde"]) # mypy error: Argument 1 to "append" of "MutableSequence" has incompatible type "List[str]"; expected "str"
The definition itself is accepted by mypy. But it does not have the desired effect.
Since Sequence
is already generic, one can directly use a type variable:
from typing import TypeVar, Sequence, Union
T = TypeVar('T')
# [T, ...] | [[T, ...], ...]
RecurSeqOf = Sequence[Union[T, Sequence[T]]]
# T | [T, ...] | [[T, ...], ...]
RecurSeqOfUnion = Union[RecurSeqOf[T], T]
This is what the documentation calls a "User defined generic type alias". RecurSeqOf = ...
defines an alias, and Sequence[Union[T, Sequence[T]]]
is generic.
This allows to define recursive types of fixed but arbitrary depth:
a0: RecurSeqOf[int]
a1: RecurSeqOf[RecurSeqOfUnion[int]]
a2: RecurSeqOf[RecurSeqOfUnion[RecurSeqOfUnion[int]]]
reveal_type(a0) # typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]]
reveal_type(a1) # typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]]
reveal_type(a2) # typing.Sequence[Union[typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]], typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int, typing.Sequence[Union[typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]], typing.Sequence[Union[builtins.int, typing.Sequence[builtins.int]]], builtins.int]]]]