Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get inner type from concrete type associated with a TypeVar

Tags:

python

mypy

I'm using mypy and the typing module in python. Imagine I have a generic type:

ContainerT = TypeVar('ContainerT')

class Thingy(Generic[ContainerT]):
    pass

However I want to get at another type inside the concrete type associated with the TypeVar like in the example below:

class SomeContainer(object):
    Iterator = SomeCustomIterator

ContainerT = TypeVar('ContainerT')

class Thingy(Generic[ContainerT]):
    def foo(self) -> ContainerT.Iterator:
        pass

I get an error saying that TypeVar does not have a member Iterator. Is there another way to reformulate this so I can associate one type with another?

Thank you.

like image 762
Alexander Kondratskiy Avatar asked Oct 06 '17 05:10

Alexander Kondratskiy


1 Answers

Maybe you are thinking about something like this.

from __future__ import annotations

from abc import abstractmethod
from typing import Generic, TypeVar, Iterator, Protocol

_Iterator = TypeVar('_Iterator', bound=Iterator, covariant=True)

class SomeContainer:
    iterator: Iterator[int]

    def __init__(self, iterator: Iterator[int]):
        self.iterator = iterator

    def get_iterator(self) -> Iterator[int]:
        return self.iterator


class SomeContainerProtocol(Protocol[_Iterator]):
    @abstractmethod
    def __init__(self, iterator: _Iterator):
        pass

    @abstractmethod
    def get_iterator(self) -> _Iterator:
        pass


_SomeContainer = TypeVar('_SomeContainer', bound='SomeContainerProtocol', covariant=True)


class Thingy(Generic[_SomeContainer]):
    container: _SomeContainer

    def __init__(self, container: _SomeContainer):
        self.container = container

    def foo(self: Thingy[SomeContainerProtocol[_Iterator]]) -> _Iterator:
        pass

    def bar(self) -> _SomeContainer:
        pass

thingy = Thingy(SomeContainer(range(10).__iter__()))
reveal_type(thingy) # Revealed type is '...Thingy[...SomeContainer*]'
reveal_type(thingy.foo) # Revealed type is 'def () -> typing.Iterator[builtins.int]'
reveal_type(thingy.bar) # Revealed type is 'def () ->  ...SomeContainer*'

You can annotate self with a generic protocol (available starting Python 3.8) in place of your type var and mypy will then infer the type of the iterator.

like image 176
unique2 Avatar answered Sep 19 '22 21:09

unique2