在Python中,如何处理__eq__方法,并且按照什么顺序处理?
在Python中,如何处理__eq__方法,并且按照什么顺序处理?
由于Python没有提供其比较运算符的左/右版本,它是如何决定调用哪个函数的呢?
类A(object):
def __eq__(self, other):
print "A __eq__ called"
return self.value == other
类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__函数。
我正在寻找官方的决策树。
Python中的`__eq__`方法的处理方式是什么以及处理顺序如何?
在Python中,通常情况下,`a == b`会调用`a.__eq__(b)`或`type(a).__eq__(a, b)`。具体来说,`__eq__`方法的调用顺序如下:
1. 如果`b`的类型是`a`的类型的严格子类(不是相同类型)并且具有`__eq__`方法,则调用该方法并返回比较结果(如果实现了比较);
2. 否则,如果`a`具有`__eq__`方法,则调用该方法并返回比较结果(如果实现了比较);
3. 否则,如果没有调用`b`的`__eq__`方法且其具有该方法,则调用该方法并返回比较结果(如果实现了比较);
4. 最后,进行身份比较,即与`is`操作符相同的比较。
如果方法返回`NotImplemented`,则表示该比较未实现。
在Python 2中,还存在一个`__cmp__`方法,但在Python 3中已被弃用和移除。
该算法是在C级别上处理的。具体来说,需要查看`object`类的默认`__eq__`方法以及查找和调用`__eq__`方法的代码。
默认的`__eq__`方法是在`object`类的C定义中的`object_richcompare`函数中处理的。该函数会首先检查要比较的对象是否满足以下条件:
- 不是相同类型;
- 第二个对象的类型是第一个对象类型的子类;
- 第二个对象的类型具有`__eq__`方法。
如果满足以上条件,会调用另一个对象的方法,并返回其结果(如果实现了比较)。如果该方法未实现,会继续执行其他操作。
如果以上条件不满足,会尝试查找并调用第一个对象类型的`__eq__`方法。只要结果不是`NotImplemented`,即表示该方法已实现,会返回结果。
如果以上操作都没有成功,会尝试查找并调用第二个对象类型的`__eq__`方法。只要结果不是`NotImplemented`,即表示该方法已实现,会返回结果。
最后,如果以上操作都没有成功,会进行一次默认操作,即对对象的身份进行比较(即使用`self is other`)。如果两个对象在内存中的位置相同,则返回`True`,否则返回`False`。
在比较过程中,我们首先考虑子类的比较实现,然后再尝试使用第一个对象的实现,如果没有调用第二个对象的实现,并最后使用身份比较进行相等性比较。
感谢提供2020年Python 3更新的信息。值得一提的是,数学运算符在左右操作数有不同处理方式,两者都会尝试使用对应的方法,例如`a=A()`,`a+1`会调用`a.__add__`,而`1+a`会尝试调用`A.__radd__`(如果`int`类型的`add`方法无法处理`A`类型)。关于无法处理的`1+a`如何被捕获并调用`A.__radd__`的机制,尚不清楚。
“接受的答案在这一点上是错误的”,但它们有`self.value == other`,与您的文章不同。
这是提问者的另一个错误,我已经在回答中进行了隐含的修正,并通过说明进行了明确的纠正,解释了我所做的更正。
Python中的`__eq__`方法处理方式如下:首先考虑子类的比较实现,然后尝试使用第一个对象的实现,如果没有调用第二个对象的实现,并最后使用身份比较进行相等性比较。
Python中的__eq__是如何处理的?处理顺序是什么?
在Python2.x中,当遇到a == b时,会按照以下步骤处理:
1. 如果type(b)是新式类(new-style class),并且type(b)是type(a)的子类,并且type(b)已经重写了__eq__方法,则结果为b.__eq__(a)。
2. 如果type(a)已经重写了__eq__方法(即type(a).__eq__不等于object.__eq__),则结果为a.__eq__(b)。
3. 如果type(b)已经重写了__eq__方法,则结果为b.__eq__(a)。
4. 如果以上情况都不满足,Python会继续查找__cmp__方法。如果存在__cmp__方法,则只有当__cmp__方法返回0时,对象才相等。
5. 最后,如果以上情况都不满足,Python会调用object.__eq__(a, b),只有当a和b是同一个对象时才返回True。
如果特殊方法中的任何一个返回NotImplemented,Python会假装该方法不存在。
需要注意最后一步:如果a和b都没有重载==操作符,那么a == b等同于a is b。
根据上述内容,我们可以看到Python在处理__eq__时的顺序和规则。这样设计的原因可能是为了提供更灵活的重载操作符的方式,并且兼容旧的代码。
值得注意的是,Python3文档中关于这一点的描述是错误的,具体可以参考相关的bug修复记录。
关于第一个步骤的文档在哪里?为什么设计成这样?
对于Python2来说,这一步骤的文档可能并不明确,可能是通过其他方式传播的。可能是为了兼容性和灵活性的考虑,设计了这样的处理顺序。
根据一些评论和答案,这个处理顺序可能会让人感到困惑。
另外,对于某个类型的实例来说,只在实例上定义一个绑定方法__eq__是否足够重载==操作符?
为什么查找__eq__方法的过程不是递归地沿着类层级往上查找?
以上是关于Python中__eq__的处理方式和顺序的说明。了解这些规则可以帮助我们更好地理解和使用Python中的相等性比较操作。
在Python中,__eq__方法用于比较两个对象是否相等。当使用表达式a == b进行比较时,如果对象a存在__eq__方法,则会调用该方法。__eq__方法的代码包括self.value == other,它用于比较对象的值是否相等。
然而,如果对象a是int类型的对象,而对象b属于另一个自定义的类B,则int类型的对象不知道如何与B类的对象进行比较。为了解决这个问题,Python会尝试调用类B的__eq__方法,看它是否知道如何与int类型的对象进行比较。
为了说明这个问题,可以修改代码,打印出正在比较的值:
class A(object): def __eq__(self, other): print("A __eq__ called: %r == %r ?" % (self, other)) return self.value == other class B(object): def __eq__(self, other): print("B __eq__ called: %r == %r ?" % (self, other)) return self.value == other a = A() a.value = 3 b = B() b.value = 4 a == b
运行以上代码,会输出:
A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ? B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?
以上代码输出了调用__eq__方法时比较的对象值,可以清楚地看到是先调用了对象a的__eq__方法,然后再调用了对象b的__eq__方法。
需要注意的是,上述问题在Python3中也是相关的。