在Python中为什么/什么时候`x == y`调用`y .__ eq __(x)`?

时间:2010-02-17 14:00:16

标签: python comparison operator-overloading

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. 如果左对象是内置或新式类,右边是它的子类,则在左对象的__rop__
  4. 之前尝试使用右对象的__op__

    这解释了TestStrCmp示例中的行为。 TestStrCmpstr的子类,但未实现自己的__eq__,因此__eq__的{​​{1}}在两种情况下都优先(即str由于规则1,将tsc == "b"调用为b.__eq__(tsc)

    __rop__示例中,TestStrEq在两个实例中都被调用,因为tse.__eq__TestStrEq的子类,因此优先调用它。

    str示例中,TestEq实现TestEq__eq__不会int同时被调用(规则1)。

    但我仍然不理解__eq__的第一个例子。 TestCmp不是tc的子类,因此应该调用AFAICT int,但不是。

4 个答案:

答案 0 :(得分:30)

您缺少通常行为的关键异常:当右侧操作数是左侧操作数的类的子类的实例时,首先调用右侧操作数的特殊方法。

请参阅以下文档:

http://docs.python.org/reference/datamodel.html#coercion-rules

特别是以下两段:

  

首先是对象xy   尝试了x.__op__(y)。如果不是这样的话   实施或退货   NotImplementedy.__rop__(x)是   试过。如果这也没有实现   或者返回NotImplemented,a   引发TypeError异常。但是看   以下例外:

     

上一项的例外:如果是   左操作数是一个实例   内置类型或新式类,   右操作数是一个实例   该类型的适当子类或   类和覆盖基数   __rop__()方法,右边   尝试了操作数的__rop__()方法   在左操作数的__op__()之前   方法

答案 1 :(得分:6)

实际上,在docs中,它指出:

  如果没有定义丰富的比较(见上文),

[__cmp__ c]通过比较操作进行调整。

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

答案 2 :(得分:1)

Language Reference中没有记录这些内容吗?只需快速查看一下,在定义__cmp____eq__等时,似乎会忽略__lt__。我理解要包括在父类中定义__eq__的情况。已定义str.__eq__,因此将忽略其子类上的__cmp__。 <{1}}等未定义,因此其子类的object.__eq__将被尊重。

回答澄清的问题:

  

我知道__cmp__被调用了   优先考虑__eq__,但我不是   明确为什么__cmp__被调用   偏好y.__eq__(x),当时   后者是文档所说的   发生。

文档说x.__eq__(y)将首先被调用,但它可以选择返回x.__eq__(y),在这种情况下调用NotImplemented。我不确定为什么你会对这里发生的不同事情充满信心。

你特别疑惑哪种情况?我理解你只是对y.__eq__(x)"b" == tsc案件感到困惑,对吗?在任何一种情况下,都会调用tsc == "b"。由于您没有覆盖TestStrCmp中的str.__eq__(onething, otherthing)方法,因此最终您只依赖于基本字符串方法,并且它说对象不相等。

在不知道__eq__的实现细节的情况下,我不知道str.__eq__是否会返回("b").__eq__(tsc)并让tsc有机会处理相等性测试。但即使它确实如此,你定义了TestStrCmp的方式,你仍然会得到一个错误的结果。

所以你不清楚你在这里看到的是意料之外的事。

正在发生的事情是,如果在被比较的对象的 上定义,那么Python更喜欢NotImplemented__eq__,而您期望__cmp__开启__cmp__最左边的对象在右侧对象上优先于__eq__。是吗?

答案 3 :(得分:1)

据我所知,__eq__()是一种所谓的“丰富比较”方法,并且优先于下面的__cmp__()调用比较运算符。如果未定义“丰富比较”,则会调用__cmp__()

所以在A == B:
    如果在A中定义__eq__(),则称其为     否则__cmp__()将被称为

__eq__()在'str'中定义,因此未调用__cmp__()函数。

同样的规则适用于__ne__(), __gt__(), __ge__(), __lt__()__le__()“丰富的比较”方法。