既然Python没有提供比较运算符的左/右版本,那么它如何决定调用哪个函数呢?
class A(object):
def __eq__(self, other):
print "A __eq__ called"
return self.value == other
class B(object):
def __eq__(self, other):
print "B __eq__ called"
return self.value == other
>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False
这似乎调用了两个__eq__
函数。
我在找官方的决策树。
4条答案
按热度按时间jaxagkaj1#
a == b
表达式调用A.__eq__
,因为它存在,它的代码包含self.value == other
,因为int不知道如何将自己与B比较,Python尝试调用B.__eq__
,看看它是否知道如何将自己与int比较。如果修改代码以显示正在比较的值:
它将打印:
dfty9e192#
当Python2.x看到
a == b
时,它会尝试执行以下操作。type(b)
是一个新式类,type(b)
是type(a)
的子类,并且type(b)
已经覆盖了__eq__
,那么结果是b.__eq__(a)
。type(a)
覆盖了__eq__
(即type(a).__eq__
不是object.__eq__
),则结果为a.__eq__(b)
。type(b)
覆盖了__eq__
,则结果为b.__eq__(a)
。__cmp__
的过程,如果__cmp__
存在,则两个对象相等当且仅当它返回zero
。object.__eq__(a, b)
,它是True
当且仅当a
和b
是同一个对象。如果任何一个特殊的方法返回
NotImplemented
,Python就会认为这个方法不存在。请仔细注意最后一步:如果
a
和b
都没有过载==
,则a == b
与a is b
相同。从https://eev.ee/blog/2012/03/24/python-faq-equality/开始
igetnqfo3#
此算法的Python 3更改/更新
在Python中如何处理
__eq__
?处理顺序是什么?通常理解,但不总是这样,
a == b
调用a.__eq__(b)
或type(a).__eq__(a, b)
。明确地说,评估的顺序是:
1.如果X1 M4 N1 X的类型是X1 M5 N1 X的类型的严格子类(不是相同类型)并且具有X1 M6 N1 X,则调用它并且在比较被实现的情况下返回值,
1.否则,如果
a
有__eq__
,则调用并返回比较结果,1.否则,看看我们是否没有调用B的
__eq__
,它有,如果比较成功就调用返回,1.否则,最后进行同一性比较,与
is
进行相同的比较。如果方法返回
NotImplemented
,我们就知道是否没有实现比较。(In Python 2中,有一个
__cmp__
方法被查找过,但在Python 3中它被弃用并删除了。)让我们通过让B子类化A来测试第一个检查的行为,这表明接受的答案在这一点上是错误的:
其在返回
False
之前仅打印B __eq__ called
。请注意,我还更正了问题中的一个小错误,即
self.value
与other
而不是other.value
进行比较-在此比较中,我们得到了两个对象(self
和other
),通常是相同的类型,因为我们在这里不进行类型检查(但是它们可以是不同的类型),我们需要知道它们是否相等,我们对它们是否相等的度量是检查value
属性,这必须在两个对象上完成。我们如何知道这个完整的算法?
这里的其他答案似乎不完整且已过时,因此我将更新信息 * 并 * 向您展示如何自己查找此答案。
这在C级处理。
这里我们需要查看两段不同的代码--类
object
的对象的默认__eq__
,以及查找和调用__eq__
方法的代码,不管它使用默认__eq__
还是自定义的__eq__
。默认值
__eq__
在relevant C api docs中查找
__eq__
,我们发现__eq__
由tp_richcompare
处理-在cpython/Objects/typeobject.c
的"object"
类型定义中,tp_richcompare
是在object_richcompare
中为case Py_EQ:
定义的。所以这里,如果
self == other
我们返回True
,否则我们返回NotImplemented
对象,这是任何不实现自己的__eq__
方法的object子类的默认行为。如何调用
__eq__
然后我们找到C API文档,即PyObject_RichCompare函数,它调用
do_richcompare
。然后我们看到为
"object"
C定义创建的tp_richcompare
函数被do_richcompare
调用,所以让我们更仔细地看一下。此函数中的第一个检查是针对被比较对象的条件:
__eq__
方法,然后调用对方的方法,交换参数,如果实现了,就返回值。如果那个方法没有实现,我们继续...
接下来我们看看是否可以从第一个类型查找
__eq__
方法并调用它,只要结果不是NotImplemented,也就是说,它被实现了,我们就返回它。否则,如果我们没有尝试其他类型的方法,而它在那里,我们就尝试它,如果比较实现了,我们就返回它。
最后,如果没有为任何一个类型实现它,我们将得到一个后备方案。
回退检查对象的标识,即它是否是内存中相同位置的同一对象-这与
self is other
的检查相同:结论
在比较中,我们首先考虑比较的子类实现。
然后,我们尝试与第一个对象的实现进行比较,如果没有调用,则与第二个对象的实现进行比较。
最后,我们使用同一性检验来比较是否相等。
brccelvz4#
在Python中,==操作符调用操作符左边的对象的eq方法,如果那个对象没有eq方法,Python会检查操作符右边的对象是否有eq方法,然后调用它。
在您提供的示例中,a对象有一个eq方法,b对象有一个eq方法。首先调用a对象的eq方法,然后调用b对象的eq方法。首先调用左侧对象的方法,然后调用右侧对象的方法(如果第一个方法不存在)。
如果您想定义not equal to运算符的工作方式,也可以在类中定义ne方法。
这样,Python将使用ne方法来判断left和right对象是否不相等,而不是使用eq方法并对结果求反。
一般来说,Python的运算符重载方法被设计成对称的,这意味着如果你覆盖了一个类中的eq方法,那么如果你想比较两个类的对象,你也应该覆盖另一个类中的eq方法。