Use case:
I have a class which is a list of instances of classes which extend Base
,
and those extending classes could themselves have a Generic
mixin.
I want an of_type
function which will let me filter to the concrete class.
First naive attempt:
class Base:
...
T = TypeVar('T', bound=Base)
class BaseList(list[Base]):
def of_type(self, klass: type[T]) -> list[T]:
return [m for m in self if isinstance(m, klass)]
Trivial case is fine:
class TrivialSub(Base):
...
list = BaseList()
list.append(TrivialSub())
print(list.of_type(TrivialSub)) # ok
But with Generic
mixin:
S = TypeVar('S')
class GenericSub(Base, Generic[S]):
val: S
list.append(GenericSub[int]())
print(list.of_type(GenericSub[int])) # fails:
# TypeError: Subscripted generics cannot be used with class and instance checks
Annoying, but makes sense.
Best I can come up with to satisfy type checker and at run time:
def of_type(self, klass: type[T]) -> list[T]:
maybe_origin = get_origin(klass)
klass_ = maybe_origin if maybe_origin else klass
return [cast(klass, m) for m in self if isinstance(m, klass_)]
Does anyone have any better ideas how to implement this?
I know my solution loses type safety on S
and I feel like I could also handle that with get_args
, but I wanted to see if I’m going down a rabbit hole first
Full MRE here: Narrowing with isinstance and possibly subscripted generic · GitHub