为什么/在Python中`x == y`调用`y .__ eq __(x)`?
Python文档明确指出x==y
调用x.__eq__(y)
。 不过,在很多情况下,情况恰恰相反。 在哪里logging何时或为什么发生这种情况,以及如何确定我的对象的__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
编辑:从马克·迪金森的回答和评论,它会显示:
- 丰富的比较覆盖
__cmp__
-
__eq__
是它自己的__rop__
它是__op__
(类似于__lt__
,__ge__
等) - 如果左侧对象是内build类或新式类,右侧是它的子类,则右对象的
__rop__
会在左对象的__op__
这解释了TestStrCmp
示例中的行为。 TestStrCmp
是str
一个子类,但是没有实现自己的__eq__
所以在两种情况下str
的__eq__
优先(即由于规则1, tsc == "b"
作为__rop__
调用b.__eq__(tsc)
)。
在TestStrEq
示例中,两个实例都调用了tse.__eq__
,因为TestStrEq
是str
一个子类,所以它被优先调用。
在TestEq
示例中, TestEq
实现了__eq__
而int
不会使__eq__
被调用(规则1)。
但是我仍然不明白TestCmp
第一个例子。 tc
不是int
的子类,所以应该调用AFAICT 1.__cmp__(tc)
,但不是。
您错过了常见行为的一个关键exception:当右操作数是左操作数类的子类的实例时,右操作数的特殊方法将首先调用。
请参阅以下文档:
http://docs.python.org/reference/datamodel.html#coercion-rules
特别是以下两段:
对于对象
x
和y
,首先尝试x.__op__(y)
。 如果没有实现或返回NotImplemented
,则尝试y.__rop__(x)
。 如果这还没有实现或返回NotImplemented
,则会引发TypeErrorexception。 但看到以下例外:前一项的例外:如果左操作数是内置types或新样式类的实例,右操作数是该types或类的正确子类的实例,并覆盖基的
__rop__()
方法,在左操作数的__rop__()
方法之前尝试了右操作数的__op__()
方法。
实际上,在文档中 ,它指出:
如果未定义丰富的比较(见上文),则通过比较操作来调整[
__cmp__
]。
__eq__
是一个丰富的比较方法,在TestCmp
的情况下,没有定义,因此调用__cmp__
据我所知, __eq__()
是一种所谓的“丰富比较”的方法,比较运算符优先于__cmp__()
。 如果未定义“富比较”,则调用__cmp__()
。
所以在A == B:
如果__eq__()
在A中定义,它将被调用
否则__cmp__()
将被调用
在'str'中定义了__eq__()
,所以__cmp__()
函数没有被调用。
同样的规则是__ne__(), __gt__(), __ge__(), __lt__()
和__le__()
“富比较”方法。
这是不是在语言参考中logging ? 只是从那里快速看,当__lt__
, __lt__
等被定义时, __lt__
被忽略。 我明白,要包括__eq__
在父类上定义的情况。 str.__eq__
已经被定义,因此__cmp__
在其子类中将被忽略。 object.__eq__
等没有定义,所以__cmp__
在其子类将被兑现。
针对澄清的问题:
我知道
__eq__
被优先调用__cmp__
,但我不清楚为什么y.__eq__(x)
优先于x.__eq__(y)
被调用,而后者是文档状态将会发生的情况。
文档称x.__eq__(y)
将首先被调用,但是它可以select返回NotImplemented
在这种情况下调用y.__eq__(x)
。 我不知道为什么你有信心在这里发生不同的事情。
你特别困惑哪种情况? 我理解你只是为了"b" == tsc
和tsc == "b"
情况感到困惑,对吗? 在任何一种情况下, str.__eq__(onething, otherthing)
调用str.__eq__(onething, otherthing)
。 由于您不覆盖TestStrCmp中的__eq__
方法,因此最终您只是依赖基本string方法,并且说对象不相等。
不知道str.__eq__
的实现细节,我不知道("b").__eq__(tsc)
是否会返回NotImplemented
并给tsc一个处理相等性testing的机会。 但即使这样做了,你已经定义了TestStrCmp的方式,你仍然会得到一个错误的结果。
所以目前还不清楚你在这里看到的是什么意外的。
也许正在发生的事情是,如果在被比较的任何一个对象上定义了__eq__
,则__cmp__
更倾向于__eq__
,而最后一个对象上的__cmp__
优先于右对象上的__eq__
。 是吗?