You can specify recursive types in the typing language by using type aliases and forward reference strings,
Garthoks = Union[Garthok, Iterable['Garthoks']]
Mypy supports recursive types by default since v0.990, and Pyright/Pylance since v2020.9.4.
Some types of forward references are handled by PEP 563. You can use them starting from Python 3.7 by doing
from __future__ import annotations– Konstantin
As of Python 3.12, __future__.annotations/stringifying is not necessary if the type is defined using a type statement:
type Garthoks = Garthok | Iterable[Garthoks]
Answer from gilch on Stack OverflowYou can specify recursive types in the typing language by using type aliases and forward reference strings,
Garthoks = Union[Garthok, Iterable['Garthoks']]
Mypy supports recursive types by default since v0.990, and Pyright/Pylance since v2020.9.4.
Some types of forward references are handled by PEP 563. You can use them starting from Python 3.7 by doing
from __future__ import annotations– Konstantin
As of Python 3.12, __future__.annotations/stringifying is not necessary if the type is defined using a type statement:
type Garthoks = Garthok | Iterable[Garthoks]
MyPy comes with this limitation, it does not support cyclic reference yet, but I found a way to work it round using TypeVars like so:
from typing import TypeVar, TypeAlias, Iterable, Union
T = TypeVar('T')
_Garthoks: TypeAlias = Union[T, Iterable[T]]
Garthoks: TypeAlias = _Garthoks[_Garthoks[_Garthoks[_Garthoks]]]
# you can nest it as deep as you need...
Currently, I find it to be the best solution util MyPy support this feature.
I hope it solves your problem.
Recursive Generic Type Support
Support recursive types
Generic `typing.ForwardRef` to support generic recursive types - Ideas - Discussions on Python.org
Recursive type hints
I have lists within lists with integer items (or bytes, but for example purposes ints are enough), for example: [1, 2, [3, 4], 5]
How can I go about creating a type hint for this structure?
This is what I've made:
from typing import List, Union NestedList = List[Union[int, 'NestedList']] test: NestedList = [1, 2, [3], 4] test.append([5]) test = test[-1]
But mypy fails with:
test_recursive_hints.py:7: error: Incompatible types in assignment (expression has type "int | NestedList", variable has type "NestedList") [assignment]
How to solve this problem?
TIL from this video typing a recursive flatten (by YT channel anthonywritescode) that you can now type hint recursive data & functions with generic type parameter!
# new syntax
# recursive type for nested list having elems of same type (eg. int)
type _RList[U] = list[U | _RList[U]]
def flatten[T](lst: _RList[T]) -> _RList[T]:
""" Flatten nested list.""""
return [
flatten(x) if isinstance(x, list) else x
for x in lst
]NOTE: Latest mypy type checks this new syntax, but editor / IDE may not recognize it yet.
Did you all know about this? Have you found more such cool type hinting syntax in Python?
As of mypy 0.990, mypy finally supports recursive type annotations, using the natural syntax:
from typing import Union, Dict, List
JSONVal = Union[None, bool, str, float, int, List['JSONVal'], Dict[str, 'JSONVal']]
d: JSONVal = {'a': ['b']}
mypy output:
Success: no issues found in 1 source file
Before 0.990, this would produce an error reporting a lack of recursive type support:
$ mypy asdf.py
asdf.py:3: error: Recursive types not fully supported yet, nested types replaced with "Any"
On such versions, Dict[str, Any] would be the way to go.
You can also use mutually recursive type aliases now, so you can do things like
from typing import Union, Dict, List
JSONVal = Union[None, bool, str, float, int, 'JSONArray', 'JSONObject']
JSONArray = List[JSONVal]
JSONObject = Dict[str, JSONVal]
d: JSONObject = {'a': ['b']}
Support for recursive types is now in Mypy.
As of October 2022 the implementation is provisional. You can enable it by adding the enable_recursive_aliases = true flag to pyproject.toml.
Starting from version 0.990 this will be enabled by default. Source.