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