mypy: "__eq__" несовместим с супертипом "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
бросок мипи error: Argument 1 of "__eq__" incompatible with supertype "object"
,
Но если я уберу __eq__
метод, Mypy не будет жаловаться, хотя compare
такой же как __eq__
, что я должен делать?
1 ответ
Корень проблемы в том, что __eq__
метод должен принимать любой объект: делать my_object == 3
допустим во время выполнения и всегда должен возвращать False. Вы можете убедиться в этом сами, проверив определение базового типа для object
в 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, которую вы используете, она напечатает заметку, рекомендующую вам структурировать ваш код таким образом.
Однако, проблема с этим подходом состоит в том, что mypy теперь больше не будет жаловаться, если вы делаете что-то глупое, как Person() == 3
, Технически это должно возвращать bool, но, прагматично, ваш код, вероятно, содержит ошибку, если вы сравниваете объект person с int.
К счастью, Mypy совсем недавно приобрела функцию, которая может отмечать такие ошибки: --strict-equality
, Теперь, когда вы запускаете Mypy с этим флагом, делая Person() == 3
будет делать ошибки вывода MyPy как Non-overlapping equality check (left operand type: "Person", right operand type: "int")
даже если вы определите __eq__
способом, описанным выше.
Обратите внимание, что вам нужно будет использовать последнюю версию mypy от master, чтобы использовать этот флаг, пока не будет выпущена следующая версия mypy (0.680). Это должно произойти примерно через 2-3 недели с момента написания.
Если определить __eq__
описанным выше способом вы не можете заниматься по любой причине, я бы лично рекомендовал подавить ошибку типа вместо замены Person на Any
,
В общем, сделайте это:
def __eq__(self, other: 'Person') -> bool: # type: ignore
return self.id == other.id
... может быть, вместе с кратким сообщением о том, почему вы подавляете ошибку.
Обоснование здесь заключается в том, что это определение __eq__
Строго говоря , небезопасно (это нарушает что-то, известное как принцип подстановки Лискова) - и если вам нужно сделать что-то небезопасное, вероятно, лучше явно отметить, что вы подрываете систему типов, чем скрывать ее с помощью Any.
И, по крайней мере, таким образом, вы все еще можете делать выражения Person() == 3
быть ошибка типа - если вы используете Any
, выражения как Person() == 3
будет молча печатать. На этом этапе вы можете просто использовать object
и структурируйте свой код так, чтобы он вел себя правильно.