在Python中,为什么/何时 `x==y` 会调用 `y.__eq__(x)`?

6 浏览
0 Comments

在Python中,为什么/何时 `x==y` 会调用 `y.__eq__(x)`?

Python的文档明确说明x==y会调用x.__eq__(y)。然而,在许多情况下,相反的情况是真的。有没有文档记录这种情况发生的时间或原因,我该如何确保我的对象的__cmp____eq__方法会被调用。

编辑:为了澄清,我知道__eq__优先于__cmp__,但我不清楚为什么会优先调用y.__eq__(x)而不是x.__eq__(y),后者是文档中将要发生的事情。

>>> class TestCmp(object):
...     def __cmp__(self, other):
...         print "__cmp__ got called"
...         return 0
... 
>>> class TestEq(object):
...     def __eq__(self, other):
...         print "__eq__ got called"
...         return True
... 
>>> tc = TestCmp()
>>> te = TestEq()
>>> 
>>> 1 == tc
__cmp__ got called
True
>>> tc == 1
__cmp__ got called
True
>>> 
>>> 1 == te
__eq__ got called
True
>>> te == 1
__eq__ got called
True
>>> 
>>> class TestStrCmp(str):
...     def __new__(cls, value):
...         return str.__new__(cls, value)
...     
...     def __cmp__(self, other):
...         print "__cmp__ got called"
...         return 0
... 
>>> class TestStrEq(str):
...     def __new__(cls, value):
...         return str.__new__(cls, value)
...     
...     def __eq__(self, other):
...         print "__eq__ got called"
...         return True
... 
>>> tsc = TestStrCmp("a")
>>> tse = TestStrEq("a")
>>> 
>>> "b" == tsc
False
>>> tsc == "b"
False
>>> 
>>> "b" == tse
__eq__ got called
True
>>> tse == "b"
__eq__ got called
True

编辑:从Mark Dickinson的答案和评论中可以看出:

  1. 富比较覆盖了__cmp__
  2. __eq__是它自己的__rop__到它的__op____lt____ge__等类似)
  3. 如果左对象是内置类或新式类,并且右对象是它的子类,则在左对象的__op__之前尝试右对象的__rop__

这解释了TestStrCmp示例中的行为。 TestStrCmpstr的子类,但没有实现自己的__eq__,因此在两种情况下都优先调用str__eq__(即因为规则1而调用b.__eq__(tsc)作为__rop__)。

TestStrEq示例中,两个实例都调用tse.__eq__,因为TestStrEqstr的子类,因此它的方法优先调用。

在TestEq示例中,TestEq实现了__eq__,而int没有,因此__eq__被调用了两次(规则1)。

但是我仍然不理解关于TestCmp的第一个示例。tc不是int的子类,因此根据我所知道的,应该调用1.__cmp__(tc),但是却没有被调用。

admin 更改状态以发布 2023年5月21日
0
0 Comments

实际上,在文档中,它说明:

如果没有定义丰富比较(见上文),则比较操作将调用__cmp__

__eq__是一个丰富比较方法,在TestCmp的情况下未定义,因此调用__cmp__

0
0 Comments

你忽略了通常行为的一个关键异常:当右操作数是左操作数所属类的子类的实例时,首先调用右操作数的特殊方法。请参阅以下文档: http://docs.python.org/reference/datamodel.html#coercion-rules ,特别是以下两段:对于对象x和y,首先尝试x.__op__(y)。 如果未实现或返回NotImplemented,则尝试y.__rop__(x)。 如果这也未实现或返回NotImplemented,则会引发TypeError异常。但请参见以下异常:前一项的异常:如果左操作数是内置类型或新式类的实例,并且右操作数是该类型或类的适当子类的实例并覆盖基础的__rop __()方法,则首先尝试右操作数的__rop__()方法,然后尝试左操作数的__op__()方法。

0