Generic list in NamedTuple in python3.4

91 Views Asked by At

I want to define new type which holds list of generic values. For example (simplified as much as possible):

from typing import NamedTuple, TypeVar, List
T = TypeVar('T')
MyType = NamedTuple('MyType', [('values', List[T])])

but this doesn't work and mypy reports following errors:

a.py:4: error: Type variable "a.T" is unbound
a.py:4: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class)
a.py:4: note: (Hint: Use "T" in function signature to bind "T" inside a function)
Found 1 error in 1 file (checked 1 source file)

Unfortunately, I need python3.4 compatibility (please don't ask why), so I can't use "dataclass" version.

I tried to ignore it by adding #type: ignore to MyType definition, but then mypy wants to add types even in constructions like this:

def do_stuff(x: MyType) -> None:
    for v in x.values:
        pass

saying: error: Need type annotation for 'v'.

Is there any way to make this work under python3.4 or do I have to keep adding # type: ignore?

1

There are 1 best solutions below

0
Robin Gugel On

There sadly isn't any actual good solution to this problem. NamedTuple and Generic really don't like each other sadly...

The best/only solution is to differentiate between type-checking and runtime - you still need to hack enabling generic arguments though...

from typing import NamedTuple, TypeVar, List, Generic, TYPE_CHECKING
T = TypeVar('T')

if TYPE_CHECKING:
    class MyType(Generic[T]):
        values: List[T]

        def __init__(values: List[T]):
            pass
else:
    # enabling MyType[int] needs some bad hacking though...
    _MyType = NamedTuple('MyType', [('values', list)])
    class _Helper:
        def __call__(self, *args, **kwargs) -> _MyType:
            return _MyType(*args, **kwargs)

        def __getitem__(self, _type: type) -> Type[_MyType]:
            return _MyType

     MyType = _Helper()


def foo(arg: MyType[int]): # works now
    for v in arg:
       ...

instance = MyType([1,2,3]) # works as well

This solution may lead to bad results when accessing any class/static function of tuple though...