不要混淆苹果和橘子
问题
我正在玩 __eq__
运算符和 NotImplemented
值 .
我试图理解 obj1.__eq__(obj2)
返回 NotImplemented
时会发生什么, obj2.__eq__(obj1)
也返回 NotImplemented
.
根据 Why return NotImplemented instead of raising NotImplementedError 的答案以及"LiveJournal"博客中的详细文章 How to override comparison operators in Python ,运行时应该回退到内置行为(基于 ==
和 !=
的标识) .
代码示例
但是,尝试下面的示例,似乎我为每对对象多次调用 __eq__
.
class Apple(object):
def __init__(self, color):
self.color = color
def __repr__(self):
return "<Apple color='{color}'>".format(color=self.color)
def __eq__(self, other):
if isinstance(other, Apple):
print("{self} == {other} -> OK".format(self=self, other=other))
return self.color == other.color
print("{self} == {other} -> NotImplemented".format(self=self, other=other))
return NotImplemented
class Orange(object):
def __init__(self, usage):
self.usage = usage
def __repr__(self):
return "<Orange usage='{usage}'>".format(usage=self.usage)
def __eq__(self, other):
if isinstance(other, Orange):
print("{self} == {other}".format(self=self, other=other))
return self.usage == other.usage
print("{self} == {other} -> NotImplemented".format(self=self, other=other))
return NotImplemented
>>> apple = Apple("red")
>>> orange = Orange("juice")
>>> apple == orange
<Apple color='red'> == <Orange usage='juice'> -> NotImplemented
<Orange usage='juice'> == <Apple color='red'> -> NotImplemented
<Orange usage='juice'> == <Apple color='red'> -> NotImplemented
<Apple color='red'> == <Orange usage='juice'> -> NotImplemented
False
预期的行为
我希望只有:
<Apple color='red'> == <Orange usage='juice'> -> NotImplemented
<Orange usage='juice'> == <Apple color='red'> -> NotImplemented
然后回到身份比较 id(apple) == id(orange)
- > False
.
1 回答
这是Python跟踪器中的issue #6970;它在2.7和Python 3.0和3.1中保持不变 .
这是由 two places 在执行带有
__eq__
方法的两个自定义类之间的比较时尝试直接比较和交换比较引起的 .丰富的比较通过PyObject_RichCompare() function,对于具有不同类型(间接)委托的对象,try_rich_compare() . 在此函数中,
v
和w
是左右操作数对象,由于两者都有__eq__
方法,因此该函数同时调用v->ob_type->tp_richcompare()
和w->ob_type->tp_richcompare()
.对于自定义类,tp_richcompare() slot被定义为slot_tp_richcompare() function,并且此函数再次为双方执行
__eq__
,首先self.__eq__(self, other)
然后other.__eq__(other, self)
.最后,这意味着在
try_rich_compare()
中第一次尝试调用apple.__eq__(apple, orange)
和orange.__eq__(orange, apple)
,然后调用反向,导致orange.__eq__(orange, apple)
和apple.__eq__(apple, orange)
调用self
和other
在slot_tp_richcompare()
中交换 .请注意,该问题仅限于不同自定义类的实例,其中两个类都定义了
__eq__
方法 . 如果任何一方没有这样的方法__eq__
只执行一次:如果你有两个相同类型的实例并且
__eq__
返回NotImplemented
,你甚至可以获得 six 比较:第一组两个比较是从优化尝试中调出来的;当两个实例具有相同的类型时,您只需要调用
v->tp_richcompare(v, w)
,毕竟可以跳过强制(对于数字) . 但是,当该比较失败(返回NotImplemented
)时,也会尝试标准路径 .如何在Python 2中进行比较变得相当复杂,因为仍然需要支持旧的
__cmp__
三向比较方法;在Python 3中,由于支持__cmp__
已删除,因此更容易解决问题 . 因此,修复程序从未向后移植到2.7 .