在Python类型中指定TypeVar的附加子类边界



在将类型提示与mymyy结合使用时,我想定义一个泛型类,它允许访问仅对某些泛型类可用的属性。我想知道如何在Python类型提示中定义它。

例如,我们有下面的例子:

class SomeObject:
pass
class SomeObjectWithAttr:
def __init__(self, attr):
self.attr = attr
class AppendListWithAttrs:
def __init__(self):
self._list = []
self._attrs = []
def append(self, obj, record_attr=False):
self._list.append(obj)
if record_attr:
self._attrs.append(obj.attr)
l = AppendListWithAttrs()
l.append(SomeObject())
l.append(SomeObjectWithAttr())
l.append(SomeObjectWithAttr(), record_attr=True)

我试着注释这个,但我似乎不能得到快乐。这是我最好的尝试:

from typing import Generic, TypeVar, Protocol, overload, Literal, Union
T = TypeVar("T")
class HasAttr(Protocol):
attr: str
class AppendListWithAttrs(Generic[T]):
def __init__(self):
self._list: list[T] = []
self._attrs: list[str] = []
@overload
def append(self: 'AppendListWithAttrs[HasAttr]', obj: HasAttr, record_attr: Literal[True]): ...
@overload
def append(self, obj: T, record_attr: Literal[False]): ...
def append(self, obj: T, record_attr: bool = False):
self._list.append(obj)
if record_attr:
self._attrs.append(obj.attr)

这也不完全正确,但我正在有效地寻找一种方法来告诉我T可以是任何东西,但如果它以HasAttr为界,它可以访问这个attr。也许像HasAttr子类化T,但也许我看错了。

你怎么注释这个?

这个怎么样?

from typing import overload, Literal, Union, Any, Protocol, Generic, TypeVar

class GenericObjectProto(Protocol):
pass

class ObjectWithAttrProto(GenericObjectProto, Protocol):
attr: str


T = TypeVar('T', bound=GenericObjectProto)

class AppendListWithAttrs(Generic[T]):
def __init__(self) -> None:
self._list: list[T] = []
self._attrs: list[str] = []

@overload
def append(self, obj: GenericObjectProto, record_attr: Literal[False] = False) -> None: ...

@overload
def append(self, obj: ObjectWithAttrProto, record_attr: Literal[True] = ...) -> None: ...

def append(self, obj,  record_attr: bool = False) -> None:
self._list.append(obj)
if record_attr:
self._attrs.append(obj.attr)

class SomeObject:
pass

class SomeObjectWithAttr:
def __init__(self, attr: str) -> None:
self.attr = attr

mylist = AppendListWithAttrs[SomeObject]()
mylist.append(SomeObject())
mylist.append(SomeObjectWithAttr('hello'))
mylist.append(SomeObjectWithAttr('hello'), record_attr=True)
reveal_type(mylist._list) # Revealed type is "builtins.list[__main__.SomeObject*]"

工作原理

我们可以从GenericObject协议继承ObjectWithAttr协议,从而获得一些特殊的类型提示。通过这样做,我们将ObjectWithAttr定义为GenericObject结构子类型。这意味着当我们将SomeObjectWithAttr实例添加到SomeObject实例列表时,MyPy不会报错。由于"结构继承";MyPy理解SomeObjectWithAttrSomeObject实例列表完全兼容,即使没有实际的继承。

警告

传递MyPy,但前提是您不将其与--strict设置一起使用,因为我没有在.append的具体实现中注释obj。MyPy似乎能够用现有的注释完美地推断类型,但是如果在--strict设置上按原样运行上述代码,它仍然会抱怨您遗漏了一个注释。即使有了重载,它也不喜欢你用TUnion类型注释obj(这对我来说似乎有点bug),所以如果你在--strict上运行MyPy,你可以用Any在具体实现中注释obj,或者把# type: ignore[no-untyped-def]放在行尾。

相关内容

  • 没有找到相关文章

最新更新