Оператор равенства, создающий истину для 2 различных объектов в памяти - PullRequest
0 голосов
/ 10 ноября 2018

Почему мой метод равенства выдает True , когда 2 объекта указывают и b указывают на 2 разные объекты в памяти?

import math


def main():

    point = Point(2, 3)

    print(point == Point(2, 3))

    b = Point(2, 3)

    print(id(point), id(b))


class Point:

    def __init__(self, x=0, y=0):
         self.x = x
         self.y = y

    def distance_from_origin(self):
         return math.hypot(self.x, self.y)

    def __eq__(self, other):
         return id(self.x) == id(other.x) and id(self.y) == id(other.y)

    def __repr__(self):
         return f"Point({self.x!r}, {self.y!r})"

    def __str__(self):
         return f"{self.x!r}, {self.y!r}"

если имя == ' main ': Основной ()

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Python кэширует маленькие целые числа (в диапазоне [-5, 256]), поэтому id(self.x) == id(other.x) and id(self.y) == id(other.y) равно True. Поскольку self.x и other.x являются одними и теми же объектами в памяти. Найдите другой способ сравнения этих двух объектов или избавьтесь от своего пользовательского __eq__ и используйте способ по умолчанию (в этом случае Python вернет False для point == Point(2, 3)).
См. этот ответ для получения дополнительной информации по этому вопросу.

0 голосов
/ 10 ноября 2018

id из Point объектов различны, потому что они разные объекты и для них нет механизма кэширования / интернирования (что было бы неправильно, потому что они изменчивы).

== работает, потому что при вызове == на Point, вы звоните __eq__, и это кодируется так:

def __eq__(self, other):
     return id(self.x) == id(other.x) and id(self.y) == id(other.y)

так, это неправильно , но он работает большую частьвремя из-за интернирования целых чисел от -5 до 256 в CPython (дальнейшие тесты показывают, что он работает с большими значениями, но это не гарантировано).Пример счетчика:

a = 912
b = 2345

point = Point(a, b)

print(point == Point(456*2, b))

вы получите False, даже если 456*2 == 912

Перепишите так, чтобы у вас не было сюрпризов с большими целыми числами:

def __eq__(self, other):
     return self.x == other.x and self.y == other.y

Если вы удалите этот метод __eq__, вы получите False, так как в этом случае оператор Python по умолчанию == для неизвестного объекта имеет только идентификатор объекта для выполнения сравнений.

НоЦель == - сравнить объект с содержимым , а не с идентификаторами .Кодирование метода равенства, который проверяет идентичности, может привести к неожиданностям, как показано выше.

В Python, когда люди используют ==, они ожидают, что объекты будут равны, если значения равны.Идентификационные данные - это деталь реализации, просто забудьте об этом.

(В предыдущих версиях Python вы также определили __ne__, поскольку это не означает обратное __eq__ и может привести к странным ошибкам) ​​

В двух словах : не используйте is (помимо is None идиома) или id, если вы не пишете очень сложную низкоуровневую программу с кешированием и странными вещамиили при отладке вашей программы.

...