为内存中的 2 个不同对象生成 True 的相等运算符
Equality Operator Producing True For 2 different Objects in Memory
当两个对象 point 和 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}"
if name == 'main':
主要()
Point
个对象的 id
是不同的,因为它们是不同的对象并且它们没有 cache/interning 机制(这是错误的,因为它们是可变的)。
==
之所以有效,是因为在 Point
上调用 ==
时,您调用了 __eq__
,它的编码如下:
def __eq__(self, other):
return id(self.x) == id(other.x) and id(self.y) == id(other.y)
所以,它是 错误的 ,但它大部分时间都有效,因为在 CPython 中实习了从 -5 到 256 的整数(进一步的测试表明它适用于更大的值,但不能保证)。反例:
a = 912
b = 2345
point = Point(a, b)
print(point == Point(456*2, b))
即使456*2 == 912
你也会得到False
重写为这样你就不会对大整数感到惊讶:
def __eq__(self, other):
return self.x == other.x and self.y == other.y
如果删除此 __eq__
方法,您将得到 False
,在这种情况下,Python 默认 ==
未知对象上的运算符只有对象执行比较的身份。
但是==
的目的是比较对象内容,而不是ids。编写一个测试身份的相等方法可能会导致意外,如上所示。
在 Python 中,当人们使用 ==
时,他们希望如果值相等则对象也相等。身份是一个实现细节,忘掉它吧。
(Python 的先前版本要求您也定义 __ne__
,因为它不会自动与 __eq__
相反,并且会导致奇怪的错误)
简而言之:不要使用is
(除了is None
习语)或id
,除非你正在写一个非常具有缓存和奇怪内容的复杂低级程序或在调试程序时。
Python 缓存小整数(范围 [-5, 256]),因此 id(self.x) == id(other.x) and id(self.y) == id(other.y)
是 True
。因为 self.x
和 other.x
是内存中的相同对象。找到一种不同的方式来比较这两个对象或摆脱您的自定义 __eq__
并使用默认方式(Python will return False
for point == Point(2, 3)
in that例)。
有关此问题的更多信息,请参阅 this 答案。
当两个对象 point 和 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}"
if name == 'main': 主要()
Point
个对象的 id
是不同的,因为它们是不同的对象并且它们没有 cache/interning 机制(这是错误的,因为它们是可变的)。
==
之所以有效,是因为在 Point
上调用 ==
时,您调用了 __eq__
,它的编码如下:
def __eq__(self, other):
return id(self.x) == id(other.x) and id(self.y) == id(other.y)
所以,它是 错误的 ,但它大部分时间都有效,因为在 CPython 中实习了从 -5 到 256 的整数(进一步的测试表明它适用于更大的值,但不能保证)。反例:
a = 912
b = 2345
point = Point(a, b)
print(point == Point(456*2, b))
即使456*2 == 912
False
重写为这样你就不会对大整数感到惊讶:
def __eq__(self, other):
return self.x == other.x and self.y == other.y
如果删除此 __eq__
方法,您将得到 False
,在这种情况下,Python 默认 ==
未知对象上的运算符只有对象执行比较的身份。
但是==
的目的是比较对象内容,而不是ids。编写一个测试身份的相等方法可能会导致意外,如上所示。
在 Python 中,当人们使用 ==
时,他们希望如果值相等则对象也相等。身份是一个实现细节,忘掉它吧。
(Python 的先前版本要求您也定义 __ne__
,因为它不会自动与 __eq__
相反,并且会导致奇怪的错误)
简而言之:不要使用is
(除了is None
习语)或id
,除非你正在写一个非常具有缓存和奇怪内容的复杂低级程序或在调试程序时。
Python 缓存小整数(范围 [-5, 256]),因此 id(self.x) == id(other.x) and id(self.y) == id(other.y)
是 True
。因为 self.x
和 other.x
是内存中的相同对象。找到一种不同的方式来比较这两个对象或摆脱您的自定义 __eq__
并使用默认方式(Python will return False
for point == Point(2, 3)
in that例)。
有关此问题的更多信息,请参阅 this 答案。