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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2281222/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-04 00:15:23  来源:igfitidea点击:

Why/When in Python does `x==y` call `y.__eq__(x)`?

pythoncomparisonoperator-overloading

提问by Singletoned

The Python docs clearly state that x==ycalls x.__eq__(y). However it seems that under many circumstances, the opposite is true. Where is it documented when or why this happens, and how can I work out for sure whether my object's __cmp__or __eq__methods are going to get called.

Python 文档清楚地说明x==y调用x.__eq__(y). 然而,似乎在许多情况下,情况恰恰相反。它在哪里记录何时或为什么发生这种情况,以及我如何确定我的对象__cmp____eq__方法是否会被调用。

Edit: Just to clarify, I know that __eq__is called in preferecne to __cmp__, but I'm not clear why y.__eq__(x)is called in preference to x.__eq__(y), when the latter is what the docs state will happen.

编辑:只是为了澄清,我知道__eq__在 preferredecne to 中调用__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

Edit: From Mark Dickinson's answer and comment it would appear that:

编辑:从马克狄金森的回答和评论看来:

  1. Rich comparison overrides __cmp__
  2. __eq__is it's own __rop__to it's __op__(and similar for __lt__, __ge__, etc)
  3. If the left object is a builtin or new-style class, and the right is a subclass of it, the right object's __rop__is tried before the left object's __op__
  1. 丰富的比较覆盖 __cmp__
  2. __eq__是它自己的__rop__,以它的__op__(以及类似的__lt____ge__等等)
  3. 如果左对象是内置类或新式类,而右对象__rop__是它的子类,则在左对象之前尝试右对象__op__

This explains the behaviour in theTestStrCmpexamples. TestStrCmpis a subclass of strbut doesn't implement its own __eq__so the __eq__of strtakes precedence in both cases (ie tsc == "b"calls b.__eq__(tsc)as an __rop__because of rule 1).

这解释了TestStrCmp示例中的行为。 TestStrCmp是 of 的子类,str但不实现自己的,__eq__因此在这两种情况下__eq__ofstr优先(即,由于规则 1tsc == "b"调用b.__eq__(tsc)为 an __rop__)。

In the TestStrEqexamples, tse.__eq__is called in both instances because TestStrEqis a subclass of strand so it is called in preference.

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

In the TestEqexamples, TestEqimplements __eq__and intdoesn't so __eq__gets called both times (rule 1).

TestEq示例中,TestEq实现__eq__int没有__eq__被两次调用(规则 1)。

But I still don't understand the very first example with TestCmp. tcis not a subclass on intso AFAICT 1.__cmp__(tc)should be called, but isn't.

但我仍然不明白第一个带有TestCmp. tc不是子类,int所以 AFAICT1.__cmp__(tc)应该被调用,但不是。

采纳答案by Mark Dickinson

You're missing a key exception to the usual behaviour: when the right-hand operand is an instance of a subclass of the class of the left-hand operand, the special method for the right-hand operand is called first.

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

See the documentation at:

请参阅以下位置的文档:

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

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

and in particular, the following two paragraphs:

尤其是以下两段:

For objects xand y, first x.__op__(y)is tried. If this is not implemented or returns NotImplemented, y.__rop__(x)is tried. If this is also not implemented or returns NotImplemented, a TypeError exception is raised. But see the following exception:

Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base's __rop__()method, the right operand's __rop__()method is tried before the left operand's __op__()method.

对于对象xand y,首先 x.__op__(y)尝试。如果这没有实现或返回 NotImplementedy.__rop__(x)则尝试。如果这也未实现或返回NotImplemented,则会引发 TypeError 异常。但看到以下异常:

上一项的例外:如果左操作数是内置类型或新式类的实例,而右操作数是该类型或类的适当子类的实例并覆盖基类的 __rop__()方法,则右操作数操作的__rop__()方法是左操作的尝试过__op__()的方法。

回答by Dancrumb

Actually, in the docs, it states:

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

[__cmp__is c]alled by comparison operations if rich comparison (see above) is not defined.

__cmp__如果未定义丰富的比较(见上文),则[由比较操作调用。

__eq__is a rich comparison method and, in the case of TestCmp, is not defined, hence the calling of __cmp__

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

回答by dubiousjim

Is this not documented in the Language Reference? Just from a quick look there, it looks like __cmp__is ignored when __eq__, __lt__, etc are defined. I'm understanding that to include the case where __eq__is defined on a parent class. str.__eq__is already defined so __cmp__on its subclasses will be ignored. object.__eq__etc are not defined so __cmp__on its subclasses will be honored.

这在语言参考中没有记录吗?只是从那里快速看一下,__cmp__当, 等被定义时__eq__,它看起来像是被忽略了__lt__。我理解包括在__eq__父类上定义的情况。str.__eq__已定义,因此__cmp__其子类将被忽略。object.__eq__等没有定义,所以__cmp__它的子类将被尊重。

In response to the clarified question:

在回答澄清的问题时:

I know that __eq__is called in preferecne to __cmp__, but I'm not clear why y.__eq__(x)is called in preference to x.__eq__(y), when the latter is what the docs state will happen.

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

Docs say x.__eq__(y)will be called first, but it has the option to return NotImplementedin which case y.__eq__(x)is called. I'm not sure why you're confident something different is going on here.

文档说x.__eq__(y)将首先被调用,但它可以选择NotImplementedy.__eq__(x)调用的情况下返回。我不知道为什么你有信心这里正在发生一些不同的事情。

Which case are you specifically puzzled about? I'm understanding you just to be puzzled about the "b" == tscand tsc == "b"cases, correct? In either case, str.__eq__(onething, otherthing)is being called. Since you don't override the __eq__method in TestStrCmp, eventually you're just relying on the base string method and it's saying the objects aren't equal.

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

Without knowing the implementation details of str.__eq__, I don't know whether ("b").__eq__(tsc)will return NotImplementedand give tsc a chance to handle the equality test. But even if it did, the way you have TestStrCmp defined, you're still going to get a false result.

在不知道 的实现细节的情况下str.__eq__,不知道是否("b").__eq__(tsc)会返回NotImplemented并给 tsc 一个处理相等测试的机会。但即使这样做了,按照您定义 TestStrCmp 的方式,您仍然会得到错误的结果。

So it's not clear what you're seeing here that's unexpected.

所以不清楚你在这里看到了什么出乎意料的。

Perhaps what's happening is that Python is preferring __eq__to __cmp__if it's defined on eitherof the objects being compared, whereas you were expecting __cmp__on the leftmost object to have priority over __eq__on the righthand object. Is that it?

也许有什么情况是,Python是宁愿__eq____cmp__,如果它定义任何对象进行比较,而您所期望__cmp__的最左边的对象转移到优先__eq__右手边的对象上。是这样吗?

回答by Mikhail Churbanov

As I know, __eq__()is a so-called “rich comparison” method, and is called for comparison operators in preference to __cmp__()below. __cmp__()is called if "rich comparison" is not defined.

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

So in A == B:
If __eq__()is defined in A it will be called
Else __cmp__()will be called

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

__eq__()defined in 'str' so your __cmp__()function was not called.

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

The same rule is for __ne__(), __gt__(), __ge__(), __lt__()and __le__()"rich comparison" methods.

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