python d1==d2何时不等价于d1.__eq__(d2)?

wmomyfyw  于 2023-03-11  发布在  Python
关注(0)|答案(3)|浏览(133)

根据文档(Python 3.8中):
默认情况下,object使用is实现__eq__(),在比较结果为假的情况下返回NotImplementedTrue if x is y else NotImplemented .
并且:
运算符符号和方法名称之间的对应关系如下:[...] x==y调用x.__eq__(y)
所以我希望

  1. ==等同于__eq__(),并且
    1.自定义类没有显式定义__eq__,当使用==比较类的两个不同示例时返回NotImplemented。但在下面的示例中,==比较返回False,而__eq__()返回NotImplemented
class Dummy():
    def __init__(self, a):
        self.a = a

d1 = Dummy(3)
d2 = Dummy(3)

d1 == d2 # False
d1.__eq__(d2) # NotImplemented

为什么?

ej83mcc0

ej83mcc01#

原因是如果操作的 * 一 * 方不能(或不愿意)提供答案,另一方就有发言权来处理这个比较,一个常见的例子是float/int比较:

>>> 1 == 1.0
True
>>> (1).__eq__(1.0)
NotImplemented
>>> (1.0).__eq__(1)
True

对于intfloat,它们都不是对方的子类,并且int对于它是否等于某个float没有任何表示,相等性比较由float类型处理。
如果您为左侧和右侧设置不同的类型,并在钩子中添加一些调试输出,则会更容易理解:

class Left:
    def __eq__(self, other):
        print("trying Left.__eq__")
        return NotImplemented

class Right:
    def __eq__(self, other):
        print("trying Right.__eq__")
        return True

演示:

>>> d1 = Left()
>>> d2 = Right()
>>> d1.__eq__(d2)
trying Left.__eq__
NotImplemented
>>> d2.__eq__(d1)
trying Right.__eq__
True
>>> d1 == d2  # Left.__eq__ "opts out" of the comparison, Python asks other side
trying Left.__eq__
trying Right.__eq__
True
>>> d2 == d1  # Right.__eq__ returns a result first, Left.__eq__ isn't considered
trying Right.__eq__
True

左边的type(d1).__eq__通过返回NotImplemented选择退出,这给了右边一个“第二次机会”来处理这个操作,如果左边返回的是False而不是NotImplemented,Python根本不会尝试右边,d1 == d2的结果将是False,如果两边都返回NotImplemented,就像在Dummy示例中一样,除非对象完全相同(即相同的示例),否则它们将被视为不相等。

uxh89sit

uxh89sit2#

这里的文档不是很清楚,肯定需要改进。
当一个Python的“内部”方法返回NotImplemented时,它通常意味着“找一些其他的方法来做这个操作”。当它找不到“一些其他的方法......”时,Python会做什么取决于操作,并且没有像它应该做的那样被很好地记录下来。因此,例如,如果x.__lt__(y)返回NotImplemented,Python可能会尝试调用y.__gt__(x)
==的情况下,如果x.__eq__(y)y.__eq__(x)都返回NotImplemented,Python返回False,这应该被更好地记录下来。

3pvhb19x

3pvhb19x3#

如果左操作数未实现.__eq__(),则==测试右操作数.__eq__()

如果左操作数.__eq__()未实现.__eq__(),则由==测试右操作数.__eq__();如果两者都返回NotImplemented,则==返回false。
您可以通过更改类来查看此行为:

class Dummy():
    def __eq__(self, o):
        print(f'{id(self)} eq')
        return NotImplemented

d1 = Dummy()
d2 = Dummy()
print(d1 == d2)
# 2180936917136 eq
# 2180936917200 eq
# False

这是操作符的常见行为,Python测试左操作数是否有实现,如果没有,Python调用右操作数的相同或反射(例如,如果x.__lt__(y)没有实现,则调用y.__gt__(x))操作符。

class Dummy:
    def __eq__(self, o):
        print(f"{id(self)} eq")
        return NotImplemented

    def __lt__(self, o):
        """reflexion of gt"""
        print(f"{id(self)} lt")
        return NotImplemented

    def __gt__(self, o):
        """reflexion of lt"""
        print(f"{id(self)} gt")
        return False

    def __le__(self, o):
        """reflexion of ge"""
        print(f"{id(self)} le")
        return NotImplemented

    def __ge__(self, o):
        """reflexion of le"""
        print(f"{id(self)} ge")
        return False


d1 = Dummy()
d2 = Dummy()

print(d1 == d2)
# 2480053379984 eq
# 2480053380688 eq
# False

print(d1 < d2)
# 2480053379984 lt
# 2480053380688 gt
# False

print(d1 <= d2)
# 2480053379984 le
# 2480053380688 ge
# False

**!**右前左例外:

如果操作数属于不同类型,并且右操作数的类型是左操作数类型的直接或间接子类,则右操作数的反射方法具有优先级,否则左操作数的方法具有优先级。

相关问题