mypy: "__eq__" несовместим с супертипом "object" - PullRequest
0 голосов
/ 21 февраля 2019

это мой код:

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 throw error: Argument 1 of "__eq__" incompatible with supertype "object".

Но если я уберу метод __eq__, mypy не будет жаловаться на это, хотя compare совпадает с __eq__, что мне делать?

1 Ответ

0 голосов
/ 21 февраля 2019

Основная проблема заключается в том, что метод __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 и структурировать свой код так, чтобы он вел себя правильно.

...