Here is some example code:
I have my underlying generic object.
T = TypeVar("T", str, int, bytes)
class MyObj(Generic[T]):
id: T
and some implementations
class MyObjImplInt(MyObj[int]): ...
class MyObjImplStr(MyObj[str]): ...
obj_int = MyObjImplInt()
type(obj_int.id) # int
obj_str = MyObjImplStr()
type(obj_str.id) # str
Now I have built a manager for these objects
TObj = TypeVar("TObj", bound=MyObj)
class MyObjMgr(Generic[TObj, T]):
objs: list[TObj]
def __init__(self, objs: list[TObj]):
self.objs = objs
def get_by_id(self, obj_id: T):
return [e for e in self.objs if e.id == obj_id]
class MyObjMgrInts(MyObjMgr[MyObjImplInt, int]): ...
class MyObjMgrStrs(MyObjMgr[MyObjImplStr, str]): ...
My issue is that I have to provide the type T
for MyObjMgr(Generic[TObj, T])
in order to specify the id
type of the underlying object. Is there a way to infer T
from TObj[T]
?
i.e. just write this:
class MyObjMgr2(Generic[TObj]):
objs: list[TObj]
def __init__(self, objs: list[TObj]):
self.objs = objs
def get_by_id(self, obj_id: T@TObj): # err here
return [e for e in self.objs if e.id == obj_id]
class MyObjMgr2Ints(MyObjMgr2[MyObjImplInt]): ...
Here is some example code:
I have my underlying generic object.
T = TypeVar("T", str, int, bytes)
class MyObj(Generic[T]):
id: T
and some implementations
class MyObjImplInt(MyObj[int]): ...
class MyObjImplStr(MyObj[str]): ...
obj_int = MyObjImplInt()
type(obj_int.id) # int
obj_str = MyObjImplStr()
type(obj_str.id) # str
Now I have built a manager for these objects
TObj = TypeVar("TObj", bound=MyObj)
class MyObjMgr(Generic[TObj, T]):
objs: list[TObj]
def __init__(self, objs: list[TObj]):
self.objs = objs
def get_by_id(self, obj_id: T):
return [e for e in self.objs if e.id == obj_id]
class MyObjMgrInts(MyObjMgr[MyObjImplInt, int]): ...
class MyObjMgrStrs(MyObjMgr[MyObjImplStr, str]): ...
My issue is that I have to provide the type T
for MyObjMgr(Generic[TObj, T])
in order to specify the id
type of the underlying object. Is there a way to infer T
from TObj[T]
?
i.e. just write this:
class MyObjMgr2(Generic[TObj]):
objs: list[TObj]
def __init__(self, objs: list[TObj]):
self.objs = objs
def get_by_id(self, obj_id: T@TObj): # err here
return [e for e in self.objs if e.id == obj_id]
class MyObjMgr2Ints(MyObjMgr2[MyObjImplInt]): ...
Share
Improve this question
edited Mar 20 at 14:09
InSync
11.1k4 gold badges18 silver badges56 bronze badges
asked Mar 20 at 13:51
TobyStackTobyStack
3211 gold badge4 silver badges16 bronze badges
3
|
2 Answers
Reset to default 2If the type of T
is important, rather than the wanting the objs
list to be a uniform implementation of MyObj[T]
then you can just use Generic[T]
and type the list as list[MyObj[T]]
:
class MyObjMgr(Generic[T]):
objs: list[MyObj[T]]
def __init__(self, objs: list[MyObj[T]]):
self.objs = objs
def get_by_id(self, obj_id: T):
return [e for e in self.objs if e.id == obj_id]
fiddle
You said you wanted one manager to be able to manage different instances of MyObj
with different generic type T
, i.e. MyObj[int]
and MyObj[str]
in the same manager instances. It follows that it's not the same type T
for all of them so it can't be specified directly (or it will be the same).
But what you can do is use a Union type like this: U = str | int | bytes
. It will allow you to accept all the different types.
Here's a full example:
from typing import Generic, TypeVar, reveal_type
T = TypeVar("T", str, int, bytes)
U = str | int | bytes
class MyObj(Generic[T]):
id: T
def __init__(self, id: T):
self.id = id
obj_int = MyObj[int](1)
reveal_type(obj_int.id) # int
obj_str = MyObj[str]('a')
reveal_type(obj_str.id) # str
class MyObjMgr:
objs: list[MyObj[U]]
def __init__(self, objs: list[MyObj[U]]):
self.objs = objs
def get_by_id(self, obj_id: U):
return [e for e in self.objs if e.id == obj_id]
def add_obj(self, obj: MyObj[U]):
self.objs.append(obj)
mgr = MyObjMgr([obj_int, obj_str])
mgr.get_by_id(1)
btw. I also needed to add an __init__
method to MyObj
otherwise we get an AttributeError
looking for the id, because it's not initialized.
Hope this helps!
T@TObj
looks like the kind of private identifier generated bymypy
during type-checking; it's not a valid Python type hint. (T@TObj
would be an expression representingT.__matmul__(TObj)
, butTypeVar
does not implement__matmul__
). – chepner Commented Mar 24 at 13:12