mypy:“__eq__”与超类型 "object" 不兼容
mypy: "__eq__" incompatible with supertype "object"
这是我的代码:
class Person:
def __init__(self, id):
self.id = id
def __eq__(self, other: 'Person') -> bool:
return self.id == other.id
def compare(self, other: 'Person') -> bool:
return self.id == other.id
mypy 抛出 error: Argument 1 of "__eq__" incompatible with supertype "object"
。
但是如果我删除 __eq__
方法,虽然 compare
与 __eq__
相同,但 mypy 不会抱怨它,我该怎么办?
根本问题是 __eq__
方法应该接受任何对象:my_object == 3
在 运行 时是合法的,并且应该总是 return False。您可以通过检查 object
in Typeshed 的基线类型定义来亲眼看到:__eq__
的签名为 def __eq__(self, o: object) -> bool: ...
因此,为了完成这项工作,实施 __eq__
的正确方法是执行以下操作:
def __eq__(self, other: object) -> bool:
if not isinstance(other, Person):
# If we return NotImplemented, Python will automatically try
# running other.__eq__(self), in case 'other' knows what to do with
# Person objects.
return NotImplemented
return self.id == other.id
事实上,如果您更新正在使用的 mypy 版本,它会打印出一条注释,建议您以这种方式构建代码。
然而,这种方法的问题是,如果你做了像 Person() == 3
这样愚蠢的事情,mypy 现在将不再抱怨。从技术上讲,这应该是 return 布尔值,但实际上,如果您将 person 对象与 int 进行比较,则您的代码可能存在错误。
值得庆幸的是,mypy 最近获得了可以标记此类错误的功能:--strict-equality
。现在,当您使用该标志 运行 mypy 时,即使您以上述方式定义 __eq__
,执行 Person() == 3
也会使 mypy 输出错误,例如 Non-overlapping equality check (left operand type: "Person", right operand type: "int")
。
请注意,在下一个版本的 mypy (0.680) 发布之前,您需要使用 master 的最新版本的 mypy 才能使用此标志。在撰写本文时,这应该会在大约 2 到 3 周内发生。
如果以上述方式定义 __eq__
无论出于何种原因您都无法做到,我个人建议抑制类型错误,而不是将 Person 替换为 Any
.
所以基本上,这样做:
def __eq__(self, other: 'Person') -> bool: # type: ignore
return self.id == other.id
...可能会附上一条简短的说明,说明您为什么要抑制错误。
这里的基本原理是 __eq__
的这个定义严格来说 是 不安全的(它违反了被称为 Liskov 替换原则的东西)——如果你需要做一些不安全的事情,最好明确标记你正在颠覆类型系统,而不是使用 Any 隐藏它。
至少这样,您仍然可以使 Person() == 3
等表达式成为类型错误——如果您使用 Any
,Person() == 3
等表达式将自动进行类型检查。到那时,您不妨只使用 object
并构建您的代码以使其正常运行。
这是我的代码:
class Person:
def __init__(self, id):
self.id = id
def __eq__(self, other: 'Person') -> bool:
return self.id == other.id
def compare(self, other: 'Person') -> bool:
return self.id == other.id
mypy 抛出 error: Argument 1 of "__eq__" incompatible with supertype "object"
。
但是如果我删除 __eq__
方法,虽然 compare
与 __eq__
相同,但 mypy 不会抱怨它,我该怎么办?
根本问题是 __eq__
方法应该接受任何对象:my_object == 3
在 运行 时是合法的,并且应该总是 return False。您可以通过检查 object
in Typeshed 的基线类型定义来亲眼看到:__eq__
的签名为 def __eq__(self, o: object) -> bool: ...
因此,为了完成这项工作,实施 __eq__
的正确方法是执行以下操作:
def __eq__(self, other: object) -> bool:
if not isinstance(other, Person):
# If we return NotImplemented, Python will automatically try
# running other.__eq__(self), in case 'other' knows what to do with
# Person objects.
return NotImplemented
return self.id == other.id
事实上,如果您更新正在使用的 mypy 版本,它会打印出一条注释,建议您以这种方式构建代码。
然而,这种方法的问题是,如果你做了像 Person() == 3
这样愚蠢的事情,mypy 现在将不再抱怨。从技术上讲,这应该是 return 布尔值,但实际上,如果您将 person 对象与 int 进行比较,则您的代码可能存在错误。
值得庆幸的是,mypy 最近获得了可以标记此类错误的功能:--strict-equality
。现在,当您使用该标志 运行 mypy 时,即使您以上述方式定义 __eq__
,执行 Person() == 3
也会使 mypy 输出错误,例如 Non-overlapping equality check (left operand type: "Person", right operand type: "int")
。
请注意,在下一个版本的 mypy (0.680) 发布之前,您需要使用 master 的最新版本的 mypy 才能使用此标志。在撰写本文时,这应该会在大约 2 到 3 周内发生。
如果以上述方式定义 __eq__
无论出于何种原因您都无法做到,我个人建议抑制类型错误,而不是将 Person 替换为 Any
.
所以基本上,这样做:
def __eq__(self, other: 'Person') -> bool: # type: ignore
return self.id == other.id
...可能会附上一条简短的说明,说明您为什么要抑制错误。
这里的基本原理是 __eq__
的这个定义严格来说 是 不安全的(它违反了被称为 Liskov 替换原则的东西)——如果你需要做一些不安全的事情,最好明确标记你正在颠覆类型系统,而不是使用 Any 隐藏它。
至少这样,您仍然可以使 Person() == 3
等表达式成为类型错误——如果您使用 Any
,Person() == 3
等表达式将自动进行类型检查。到那时,您不妨只使用 object
并构建您的代码以使其正常运行。