我找不到任何详细涵盖这一点的问题,因此将尝试解释。
对于类型检查来说“相同类型”意味着什么
好吧,这可能是“你不能,也不应该”的长形式。
类型检查的目的是确认所有函数都使用正确的参数类型调用并返回预期的类型。我建议阅读PEP483首先要理解概念type更好的。假设您有以下内容:
s1 = ''.join(['a', 'b', 'c'])
s2 = ''.join(['a', 'b', 'c'])
assert s1 is not s2
assert s1 == s2
(join
以避免优化,但这是另一个故事)。它们是同一个对象吗?不,is not
清楚地说明了这一点(它们具有不同的内存地址)。但会s2
随时可以接受s1
?绝对是的。您不会创建仅在以下位置运行的函数s1
并检查这个事实is
, right?
现在是什么不同之处之间self
作为对确切对象的引用和self
as any A
实例?当我们谈论类型检查时,所有A
实例是完全等价且不可区分的。它们具有相同的方法和属性集(包括类型)。我们可以问:“如果我们显式声明对象为self
实例而不仅仅是self
类型?”我真的想不出任何类型。如果你想要这个语义,请使用文档字符串 - 类型不应该被滥用于所有事情。self
对象与任何其他对象完全相同A()
类型检查器的实例。
简单的解决方案:返回类实例
您的第一个代码示例几乎没问题。将返回注释为A
告诉它返回类的实例A
,它将适用于最后一堂课:
class A:
def foo(self) -> A:
return self
然而,这种方法有一个缺点(在 PEP673 中很好地解释了Self
type):
class AChild(A):
pass
# mypy
reveal_type(AChild().foo()) # N: revealed type is "__main__.A"
如果您创建新的A
in foo
并返回,那么这个方法就完美了。如果你return self
- 它是有效的,但不精确。这就是为什么我们需要Self
type.
Self
type
Self
类型被引入PEP673,并且不被支持mypy
在撰写本文时。 (更新:自支持以来mypy 1.0发布于 2023 年 2 月 6 日)您在第三个示例中的用法完全有效,并且在类型检查器中实现后将起作用(请参阅 PEP 的“动机”部分中的第 5 个代码块)。该支持已添加到mypy
在版本中1.0
,所以如果您使用此版本或更高版本 - 本节是正确的答案。
以下是您可以使用的方法Self
(假设python=3.11
不去打扰typing_extensions
):
from typing import Self
class A:
def foo(self) -> Self:
return self
class AChild(A):
pass
# mypy
reveal_type(AChild().foo()) # N: revealed type is "__main__.AChild"
reveal_type(A().foo()) # N: revealed type is "__main__.A"
实施Self
键入而不使用它
不过,你可以模仿Self
准确地用几行代码(和python >= 3.7
, 公平)。
from typing import TypeVar
_Self = TypeVar('_Self', bound='A')
class A:
def foo(self: _Self) -> _Self:
return self
class AChild(A):
pass
# mypy
reveal_type(AChild().foo()) # N: revealed type is "__main__.AChild"
reveal_type(A().foo()) # N: revealed type is "__main__.A"
现在这有效了。所有子类都会返回它们的类实例。