此问题源于本地拒绝失败的测试失败,并且只会在我们的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
时,两个安装的比较行为相同,并给出与上述本地结果相同的结果。
我刚刚发现我可以使用__int__
和__gt__
方法进一步简化测试用例,但是如果我删除其中任何一种方法,那么奇怪的行为就会消失。
答案 0 :(得分:5)
如评论中所述,dict
已经定义了所有比较运算符。 documented行为是:
不同类型的对象,除了不同的数字类型和不同的字符串类型,从不比较相等;这些对象是一致但任意地排序
换句话说,dicts被明确定义为允许与其他类型的比较,但是这种比较的结果是未定义的。 (这在Python 3中已更改,因此不再允许这些类型的类型间比较。)
当你只覆盖你的类型的一些比较运算符时,你会更复杂化。由于您的类型定义__gt__
但不定义__lt__
,thing > 0.01
将使用您的自定义__gt__
,但thing < 0.01
将使用默认(未定义)比较行为。因此,您获得的类型有时会使用确定性规则,有时会提供未定义的行为,具体取决于您使用的比较运算符。我不知道为什么你会看到你所看到的精确结果模式,但最重要的是你的班级依赖于未定义的行为,所以你不能指望使用这种类型的比较有任何一致性。 。 Python的两个实现可能在一些产生不同未定义行为的神秘实现级别上做了不同的事情。未定义行为的一点是你不应该知道它是如何工作的(或者你可能会开始依赖它)。
顺便说一下,total_ordering
这里是一个无操作,如果删除它,行为应该是相同的。 total_ordering
仅添加尚未定义的比较运算符,但dict
已经定义了所有比较运算符,因此total_ordering
不会做任何事情。如果要在已定义其自己的比较行为(如dict)的类型的子类上创建自己的排序关系,则需要手动覆盖每个单独的比较运算符。
答案 1 :(得分:1)
经过进一步调查,根据@BrenBarn的精彩回答,我找到了奇怪行为的根源。
“未定义”比较的最后一步是比较对象类型的内存位置。在本地和CI服务器上比较id(type(thing))
和id(type(0.02))
后,我发现Thing
的ID始终在本地更高,并且在CI服务器上总是更低!