假设我有一个基类
from typing import List, Optional
class Node:
def __init__(self, name: str) -> None:
self.name = name
self.children: List['Node'] = []
...
和一个子类
class PropertiesNode(Node):
def __init__(
self, name: str, properties: List[str], inherit: Optional['PropertiesNode']
) -> None:
Node.__init__(self, name)
self.properties = set(properties)
if inherit:
self.properties.update(inherit.properties)
self.children = deepcopy(inherit.children)
for child in self.children:
child.properties.update(properties)
# ^ ERR: "Node" has no attribute "properties" [attr-defined]
如您所见,mypy(正确地)标记了一个错误,因为
Node.children
被明确指定为 List[Node]
类型。
所以我阅读了泛型类型,在我看来,解决方案是使用
TypeVar
s 和 Generic
:
from typing import Generic, List, Optional, TypeVar
N = TypeVar('N', bound='Node')
P = TypeVar('P', bound='PropertiesNode')
class Node(Generic[N]):
def __init__(self: N, name: str) -> None:
self.name = name
self.children: List[N] = []
class PropertiesNode(Node[P]):
def __init__(
self: P, name: str, properties: List[str], inherit: Optional[P]
) -> None:
Node.__init__(self, name)
self.properties = set(properties)
if inherit:
self.properties.update(inherit.properties)
self.children = deepcopy(inherit.children)
for child in self.children:
child.properties.update(properties)
但是,现在当我实例化这些类时,我得到了
foo = Node("foo")
# ^ ERR Need type annotation for "foo" [var-annotated]
bar = PropertiesNode("bar", ["big", "green"], None)
# ^ ERR Need type annotation for "bar" [var-annotated]
现在,我可以通过这样做来让这些沉默
foo: Node = Node("foo")
bar: PropertiesNode = PropertiesNode(...)
但是为什么这会让它沉默——我不会在那里给我的py任何新信息?我想得越多,就越觉得
Generic
似乎是正确的选择,因为事实是:Node
或 PropertiesNode
的所有实例都将具有与 self.children
类型完全相同的 self
。
但是如果我从
Generic[N]
中删除 class Node(Generic[N]):
,我最终会再次遇到原来的错误:
class PropertiesNode(Node):
...
child.properties.update(properties)
# ^ ERR "N" has no attribute "properties" [attr-defined]
这里发生了两件事
1。泛型
注释变量
foo: Node
,其中Node
是泛型类,相当于将其注释为foo: Node[typing.Any]
。它将在默认设置下使 MyPy 静音,但如果您选择使用 MyPy,并将一些更严格的标志设置为 True
(我建议这样做!),您会发现 MyPy 仍然将此类事情标记为错误.
如果您在 MyPy 中运行此命令:
from typing import TypeVar, Generic, List
N = TypeVar('N', bound='Node')
class Node(Generic[N]):
def __init__(self: N, name: str) -> None:
self.name = name
self.children: List[N] = []
foo: Node = Node("foo")
reveal_type(foo)
您会发现 MyPy 将返回类似以下内容的消息:
Revealed type is "__main__.Node[Any]"
(注意:
reveal_type
是 MyPy 可以识别的函数,但如果您尝试在运行时使用它,则会失败。)
要让 MyPy 将未参数化的泛型标记为错误,请使用命令行参数
--disallow-any-generics
运行 MyPy。这样做意味着 MyPy 将标记以下错误:
main.py:3: error: Missing type parameters for generic type "Node"
main.py:10: error: Missing type parameters for generic type "Node"
迫使您将代码调整为以下内容:
from typing import TypeVar, Generic, List, Any
N = TypeVar('N', bound='Node[Any]')
class Node(Generic[N]):
def __init__(self: N, name: str) -> None:
self.name = name
self.children: List[N] = []
foo: Node[Any] = Node("foo")
这让 MyPy 再次高兴,并说了与您在原始代码中所说的相同的事情,但更明确。
但是...
2。我认为在这种情况下没有必要使用泛型
您不需要从泛型继承,以便用 self
注释
__init__
中的 TypeVar
参数。此外,正如您在问题中所说,无论是从 MyPy 的角度还是从阅读您代码的其他人的角度来看,从 Generic
继承在这里并没有真正的意义。我会像这样修改你的代码:from typing import List, Optional, TypeVar, Any
from copy import deepcopy
N = TypeVar('N', bound='Node')
P = TypeVar('P', bound='PropertiesNode')
class Node:
def __init__(self: N, name: str, *args: Any, **kwargs: Any) -> None:
self.name = name
self.children: List[N] = []
class PropertiesNode(Node):
def __init__(self: P, name: str, properties: List[str], inherit: Optional[P], *args: Any, **kwargs: Any) -> None:
super().__init__(name)
self.properties = set(properties)
if inherit is not None:
self.properties.update(inherit.properties)
self.children: List[P] = deepcopy(inherit.children)
for child in self.children:
child.properties.update(properties)
现在我们有了让 MyPy 满意的注释,即使是在最严格的设置下,它们甚至对人类也有意义!
注意我在这里更改了代码中的另外两件事:
我在您的