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

Python文档明确指出x==y调用x.__eq__(y) 。 不过,在很多情况下,情况恰恰相反。 在哪里logging何时或为什么发生这种情况,以及如何确定我的对象的__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 

编辑:从马克·迪金森的回答和评论,它会显示:

  1. 丰富的比较覆盖__cmp__
  2. __eq__是它自己的__rop__它是__op__ (类似于__lt____ge__等)
  3. 如果左侧对象是内build类或新式类,右侧是它的子类,则右对象的__rop__会在左对象的__op__

这解释了TestStrCmp示例中的行为。 TestStrCmpstr一个子类,但是没有实现自己的__eq__所以在两种情况下str__eq__优先(即由于规则1, tsc == "b"作为__rop__调用b.__eq__(tsc) )。

TestStrEq示例中,两个实例都调用了tse.__eq__ ,因为TestStrEqstr一个子类,所以它被优先调用。

TestEq示例中, TestEq实现了__eq__int不会使__eq__被调用(规则1)。

但是我仍然不明白TestCmp第一个例子。 tc不是int的子类,所以应该调用AFAICT 1.__cmp__(tc) ,但不是。

您错过了常见行为的一个关键exception:当右操作数是左操作数类的子类的实例时,右操作数的特殊方法将首先调用。

请参阅以下文档:

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

特别是以下两段:

对于对象xy ,首先尝试x.__op__(y) 。 如果没有实现或返回NotImplemented ,则尝试y.__rop__(x) 。 如果这还没有实现或返回NotImplemented ,则会引发TypeErrorexception。 但看到以下例外:

前一项的例外:如果左操作数是内置types或新样式类的实例,右操作数是该types或类的正确子类的实例,并覆盖基的__rop__()方法,在左操作数的__rop__()方法之前尝试了右操作数的__op__()方法。

实际上,在文档中 ,它指出:

如果未定义丰富的比较(见上文),则通过比较操作来调整[ __cmp__ ]。

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

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

所以在A == B:
如果__eq__()在A中定义,它将被调用
否则__cmp__()将被调用

在'str'中定义了__eq__() ,所以__cmp__()函数没有被调用。

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

这是不是在语言参考中logging ? 只是从那里快速看,当__lt____lt__等被定义时, __lt__被忽略。 我明白,要包括__eq__在父类上定义的情况。 str.__eq__已经被定义,因此__cmp__在其子类中将被忽略。 object.__eq__等没有定义,所以__cmp__在其子类将被兑现。

针对澄清的问题:

我知道__eq__被优先调用__cmp__ ,但我不清楚为什么y.__eq__(x)优先于x.__eq__(y)被调用,而后者是文档状态将会发生的情况。

文档称x.__eq__(y)将首先被调用,但是它可以select返回NotImplemented在这种情况下调用y.__eq__(x) 。 我不知道为什么你有信心在这里发生不同的事情。

你特别困惑哪种情况? 我理解你只是为了"b" == tsctsc == "b"情况感到困惑,对吗? 在任何一种情况下, str.__eq__(onething, otherthing)调用str.__eq__(onething, otherthing) 。 由于您不覆盖TestStrCmp中的__eq__方法,因此最终您只是依赖基本string方法,并且说对象不相等。

不知道str.__eq__的实现细节,我不知道("b").__eq__(tsc)是否会返回NotImplemented并给tsc一个处理相等性testing的机会。 但即使这样做了,你已经定义了TestStrCmp的方式,你仍然会得到一个错误的结果。

所以目前还不清楚你在这里看到的是什么意外的。

也许正在发生的事情是,如果在被比较的任何一个对象上定义了__eq__ ,则__cmp__更倾向于__eq__ ,而最后一个对象上的__cmp__优先于右对象上的__eq__ 。 是吗?