这个问题是由一个失败的测试引起的,该测试拒绝在本地失败,并且只会在我们的 CI 服务器上失败。
事实证明,一些相当狡猾的对象比较是无意中进行的。
我现在很好奇为什么相同 Python 版本 (2.7.9) 的两个安装之间的行为如此不同。
这个测试用例可能会进一步简化,但这是我得到的:
import operator
class Thing(dict):
def __int__(self, number):
return self['number']
def __gt__(self, other):
return self['number'] > other
thing = Thing({'number': 2})
for o in [
operator.lt,
operator.le,
operator.eq,
operator.ne,
operator.ge,
operator.gt]:
print o
print o(0.01, thing)
print o(thing, 0.01)
在本地运行的结果是:
<built-in function lt>
True
False
<built-in function le>
True
False
<built-in function eq>
False
False
<built-in function ne>
True
True
<built-in function ge>
False
True
<built-in function gt>
False
True
但在 Travis CI 服务器上它是:
<built-in function lt>
True
True
<built-in function le>
False
True
<built-in function eq>
False
False
<built-in function ne>
True
True
<built-in function ge>
True
False
<built-in function gt>
True
True
Python 退回到了什么样的比较行为,为什么它会在同一版本的两个安装上表现出如此不同的行为?
我最初的想法是某种基于 id
的比较,但是从 id
的值来看,它们与比较。
更新:
只有当类继承自 dict
时才会出现这种不同的行为。当它从 object
继承时,比较在两个安装上的行为相同,并给出与上述本地结果相同的结果。
更新 2:
我刚刚发现我可以仅使用 __int__
和 __gt__
方法进一步简化测试用例,但是如果我删除其中任何一个方法,那么奇怪的行为消失了。
最佳答案
如评论中所述,dict
已经定义了所有的比较运算符。 documented行为是:
Objects of different types, except different numeric types and different string types, never compare equal; such objects are ordered consistently but arbitrarily
换句话说,字典被专门定义为允许与其他类型进行比较,但此类比较的结果是未定义的。 (这在 Python 3 中已更改,因此不再允许此类类型间比较。)
当您只为您的类型覆盖一些比较运算符时,您会使事情变得更加复杂。由于您的类型定义了 __gt__
但不是 __lt__
, thing > 0.01
将使用您的自定义 __gt__
,但是thing < 0.01
将使用默认的(未定义的)比较行为。因此,您得到的类型有时会使用确定性规则,有时会给出未定义的行为,具体取决于您使用的比较运算符。我不知道为什么你会看到你所看到的结果的精确模式,但最重要的是你的类依赖于未定义的行为,所以你不能期望使用这种类型的比较有任何一致性。 Python 的两种实现可能在某些神秘的实现级别上做不同的事情,从而产生不同的未定义行为。未定义行为的要点是您不应该知道它是如何工作的(否则您可能会开始依赖它)。
顺便说一句,total_ordering
这是一个空操作,如果你删除它,行为应该是一样的。 total_ordering
仅添加尚未定义的比较运算符,但 dict
已经定义了所有这些,所以 total_ordering
不会做任何事情。如果您想在已经定义了自己的比较行为(如 dict)的类型的子类上建立自己的排序关系,那么您需要手动覆盖每个单独的比较运算符。
关于python - 从 dict 继承时不一致的对象比较行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28887383/