Python文档明确指出x==y
调用x.__eq__(y)
。然而,似乎在许多情况下,情况正好相反。哪里记录了何时或为何发生这种情况,以及如何确定我的对象的__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
编辑:从Mark Dickinson的回答和评论中可以看出:
__cmp__
__eq__
它是__rop__
自己的__op__
(__lt__
,__ge__
等相似)__rop__
__op__
醇>
这解释了TestStrCmp
示例中的行为。 TestStrCmp
是str
的子类,但未实现自己的__eq__
,因此__eq__
的{{1}}在两种情况下都优先(即str
由于规则1,将tsc == "b"
调用为b.__eq__(tsc)
。
在__rop__
示例中,TestStrEq
在两个实例中都被调用,因为tse.__eq__
是TestStrEq
的子类,因此优先调用它。
在str
示例中,TestEq
实现TestEq
而__eq__
不会int
同时被调用(规则1)。
但我仍然不理解__eq__
的第一个例子。 TestCmp
不是tc
的子类,因此应该调用AFAICT int
,但不是。
答案 0 :(得分:30)
您缺少通常行为的关键异常:当右侧操作数是左侧操作数的类的子类的实例时,首先调用右侧操作数的特殊方法。
请参阅以下文档:
http://docs.python.org/reference/datamodel.html#coercion-rules
特别是以下两段:
首先是对象
x
和y
尝试了x.__op__(y)
。如果不是这样的话 实施或退货NotImplemented
,y.__rop__(x)
是 试过。如果这也没有实现 或者返回NotImplemented
,a 引发TypeError异常。但是看 以下例外:上一项的例外:如果是 左操作数是一个实例 内置类型或新式类, 右操作数是一个实例 该类型的适当子类或 类和覆盖基数
__rop__()
方法,右边 尝试了操作数的__rop__()
方法 在左操作数的__op__()
之前 方法
答案 1 :(得分:6)
实际上,在docs中,它指出:
如果没有定义丰富的比较(见上文),[
__cmp__
c]通过比较操作进行调整。
__eq__
是一种丰富的比较方法,在TestCmp
的情况下,未定义,因此调用了__cmp__
答案 2 :(得分:1)
Language Reference中没有记录这些内容吗?只需快速查看一下,在定义__cmp__
,__eq__
等时,似乎会忽略__lt__
。我理解要包括在父类中定义__eq__
的情况。已定义str.__eq__
,因此将忽略其子类上的__cmp__
。 <{1}}等未定义,因此其子类的object.__eq__
将被尊重。
回答澄清的问题:
我知道
__cmp__
被调用了 优先考虑__eq__
,但我不是 明确为什么__cmp__
被调用 偏好y.__eq__(x)
,当时 后者是文档所说的 发生。
文档说x.__eq__(y)
将首先被调用,但它可以选择返回x.__eq__(y)
,在这种情况下调用NotImplemented
。我不确定为什么你会对这里发生的不同事情充满信心。
你特别疑惑哪种情况?我理解你只是对y.__eq__(x)
和"b" == tsc
案件感到困惑,对吗?在任何一种情况下,都会调用tsc == "b"
。由于您没有覆盖TestStrCmp中的str.__eq__(onething, otherthing)
方法,因此最终您只依赖于基本字符串方法,并且它说对象不相等。
在不知道__eq__
的实现细节的情况下,我不知道str.__eq__
是否会返回("b").__eq__(tsc)
并让tsc有机会处理相等性测试。但即使它确实如此,你定义了TestStrCmp的方式,你仍然会得到一个错误的结果。
所以你不清楚你在这里看到的是意料之外的事。
正在发生的事情是,如果在被比较的对象的 上定义,那么Python更喜欢NotImplemented
到__eq__
,而您期望__cmp__
开启__cmp__
最左边的对象在右侧对象上优先于__eq__
。是吗?
答案 3 :(得分:1)
据我所知,__eq__()
是一种所谓的“丰富比较”方法,并且优先于下面的__cmp__()
调用比较运算符。如果未定义“丰富比较”,则会调用__cmp__()
。
所以在A == B:
如果在A中定义__eq__()
,则称其为
否则__cmp__()
将被称为
__eq__()
在'str'中定义,因此未调用__cmp__()
函数。
同样的规则适用于__ne__(), __gt__(), __ge__(), __lt__()
和__le__()
“丰富的比较”方法。