Python narrowing of generic type in function signature

55 Views Asked by At

I'm having problems with generics and type narrowing in python. What I want is a generic list type and a filter function that selects elements by type. I have two goals:

  1. The type argument of the filter function should be restricted to the possible types in the generic list.
  2. The return type of the filter function should be as narrow as possible.

So far, I can only come up with solutions that achieve one of the goals but never both at once. Here is a code example:

from typing import Generic, TypeVar, Type

T = TypeVar('T')
V = TypeVar('V')


class FooList(Generic[T]):
    def __init__(self) -> None:
        self.data: list[T] = []
    def filter(self, type__: Type[T]):
        return [d for d in self.data if isinstance(d, type__)]

class BarList(Generic[T]):
    def __init__(self) -> None:
        self.data: list[T] = []
    def filter(self, type__: Type[V]) -> list[V]:
        return [d for d in self.data if isinstance(d, type__)]


lst = FooList[int | str]()
lst.filter(float) # good: does not typecheck
filtered = lst.filter(int) # bad: filtered: list[int | str]

lst = BarList[int | str]()
lst.filter(float) # bad: does typecheck
filtered = lst.filter(int) # good: filtered: list[int]

# goal:
lst = BazList[int | str]()
lst.filter(float) # good: does not typecheck
filtered = lst.filter(int) # good: filtered: list[int]

I would expect a possible solution to define the function with T as argument and a narrower V as return type, but I don't know how to express this. Binding V to T (i.e. V = TypeVar('V', bound=T)) does not seem to work, as TypeVar bound type cannot be generic.

0

There are 0 best solutions below