我正在使用__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)
- &gt; False
。
答案 0 :(得分:7)
这是Python跟踪器中的issue #6970;它在2.7和Python 3.0和3.1中保持不固定。
这是由于两个地方在执行两个使用__eq__
方法的自定义类之间进行比较时尝试直接比较和交换比较。
丰富的比较通过PyObject_RichCompare()
function,对于具有不同类型(间接)委托给try_rich_compare()
的对象。在此函数中,v
和w
是左右操作数对象,由于两者都有__eq__
方法,因此该函数同时调用v->ob_type->tp_richcompare()
和w->ob_type->tp_richcompare()
。< / p>
对于自定义类,tp_richcompare()
slot定义为slot_tp_richcompare()
function,此函数再次为双方执行__eq__
,首先self.__eq__(self, other)
然后other.__eq__(other, self)
。
最后,这意味着在apple.__eq__(apple, orange)
中首次尝试调用orange.__eq__(orange, apple)
和try_rich_compare()
,然后调用反向,从而生成orange.__eq__(orange, apple)
和{ {1}} apple.__eq__(apple, orange)
和self
之间的other
调用slot_tp_richcompare()
。
请注意,问题仅限于不同自定义类的实例,其中两个类定义__eq__
方法。如果任何一方没有这样的方法__eq__
只执行一次:
>>> class Pear(object):
... def __init__(self, purpose):
... self.purpose = purpose
... def __repr__(self):
... return "<Pear purpose='{purpose}'>".format(purpose=self.purpose)
...
>>> pear = Pear("cooking")
>>> apple == pear
<Apple color='red'> == <Pear purpose='cooking'> -> NotImplemented
False
>>> pear == apple
<Apple color='red'> == <Pear purpose='cooking'> -> NotImplemented
False
如果您有两个相同类型的 并且__eq__
返回NotImplemented
的实例,那么您甚至可以进行六次比较:
>>> class Kumquat(object):
... def __init__(self, variety):
... self.variety = variety
... def __repr__(self):
... return "<Kumquat variety=='{variety}'>".format(variety=self.variety)
... def __eq__(self, other):
... # Kumquats are a weird fruit, they don't want to be compared with anything
... print("{self} == {other} -> NotImplemented".format(self=self, other=other))
... return NotImplemented
...
>>> Kumquat('round') == Kumquat('oval')
<Kumquat variety=='round'> == <Kumquat variety=='oval'> -> NotImplemented
<Kumquat variety=='oval'> == <Kumquat variety=='round'> -> NotImplemented
<Kumquat variety=='round'> == <Kumquat variety=='oval'> -> NotImplemented
<Kumquat variety=='oval'> == <Kumquat variety=='round'> -> NotImplemented
<Kumquat variety=='oval'> == <Kumquat variety=='round'> -> NotImplemented
<Kumquat variety=='round'> == <Kumquat variety=='oval'> -> NotImplemented
False
第一组两次比较是从优化尝试中调出来的;当两个实例具有相同的类型时,您只需要调用v->tp_richcompare(v, w)
,然后可以跳过强制(对于数字)。但是,当该比较失败(返回NotImplemented
)时,标准路径也尝试。
如何在Python 2中进行比较变得相当复杂,因为仍然需要支持较旧的__cmp__
3路比较方法;在Python 3中,删除了对__cmp__
的支持,更容易解决问题。因此,修复程序从未向后移植到2.7。