'Infer type from subclass method return type
I'm trying to infer a type from the returned type of a subclass method. So far I haven't been able to make this work without making Parent a generic class (which I'd like to avoid).
The Parent class will be in a lib to be imported and create subclasses of.
Any idea how to do that ? Thanks a lot.
from __future__ import annotations
from typing import Any, Iterable, Optional, TypeVar, Type
TRetType = TypeVar("TRetType")
TChild = TypeVar("TChild", bound="Parent")
class Parent:
def run(self) -> Any:
return "foo"
@classmethod
def run_children(cls: Type[TChild], children: Iterable[TChild]) -> Iterable[TRetType]:
for child in children:
ret: TRetType = child.run() # <--- attempt to force TRetType to the return type of child.run(), in that case, int
yield ret
class Child(Parent):
def run(self) -> int: # <--- the type that children_res should be of
return 1
c = [Child() for _ in range(3)]
children_res = Child.run_children(c) # <--- children_res has no type
Solution 1:[1]
I don't think that's possible; there's no way to infer the return type of run
from just the TChild
type var.
I assume that you don't want to make Parent
generic because there's no higher-kinded types in Python? I imagine what you'd ideally do is something like:
T = TypeVar("T")
R = TypeVar("R")
TChild = HigherKindedTypeVar("TChild", bound="Parent")
class Parent(Generic[T]):
def run(self) -> T:
raise NotImplementedError
@classmethod
def run_children(cls: Type[TChild[R]], children: Iterable[TChild[R]]) -> Iterable[R]:
...
class Child(Parent[int]):
def run(self) -> int: # <--- the type that children_res should be of
return 1
(Note that the above is pseudocode)
Without higher-kinded types, you'd have to either enforce the TChild
constraint (so that children
's elements are the same type as the class), or the return type constraint (so childrens
's elements can be any Parent
subclass, as long as they return the given type). You can choose which to enforce depending on your use case.
Here's an example of the latter (mypy-play link):
T = TypeVar("T")
R = TypeVar("R")
class Parent(Generic[T]):
def run(self) -> T:
raise NotImplementedError
@classmethod
def run_children(cls: Type[Parent[R]], children: Iterable[Parent[R]]) -> Iterable[R]:
# Use `R` because `T` is already bound in class scope.
...
class IntChild1(Parent[int]):
def run(self) -> int:
return 1
class IntChild2(Parent[int]):
def run(self) -> int:
return 2
class StrChild(Parent[str]):
def run(self) -> str:
return "str"
IntChild1.run_children([IntChild1()])
IntChild1.run_children([IntChild2()])
IntChild1.run_children([StrChild()]) # error
IntChild2.run_children([IntChild1()])
IntChild2.run_children([IntChild2()])
IntChild2.run_children([StrChild()]) # error
StrChild.run_children([IntChild1()]) # error
StrChild.run_children([IntChild2()]) # error
StrChild.run_children([StrChild()])
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Zecong Hu |