最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How do I type a generic subclass correctly? - Stack Overflow

programmeradmin4浏览0评论

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
  • Do you want this Manager to be able to manage all types of MyObj regardless of their type T or do you have separate managers for the separate types? I.e. do you mix different TObj types in the same manager instance? – Gei Stoyanov Commented Mar 20 at 14:25
  • ideally one manager, as its base class to the obj it is managing, and not particular to the objs id T. For example, I could have vastly different objects which are siblings with the same id T and can be managed in the same context – TobyStack Commented Mar 20 at 15:39
  • T@TObj looks like the kind of private identifier generated by mypyduring type-checking; it's not a valid Python type hint. (T@TObj would be an expression representing T.__matmul__(TObj), but TypeVar does not implement __matmul__). – chepner Commented Mar 24 at 13:12
Add a comment  | 

2 Answers 2

Reset to default 2

If 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!

发布评论

评论列表(0)

  1. 暂无评论