从dict继承时不一致的对象比较行为

Aco*_*orn 7 python comparison-operators

此问题源于本地拒绝失败的测试失败,并且只会在我们的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)
Run Code Online (Sandbox Code Playgroud)

在本地运行它的结果是:

<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
Run Code Online (Sandbox Code Playgroud)

但在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
Run Code Online (Sandbox Code Playgroud)

Python回归到什么样的比较行为,为什么它会在同一版本的两个安装上表现出这种不同的行为?

我最初的想法是某种id基础比较,但从查看它的价值来看id,它们与比较的结果完全无关.

更新:

这种不同的行为仅在类继承时发生dict.当它继承时object,比较在两个安装上的行为相同,并给出与上面的本地结果相同的结果.

更新2:

我刚刚发现我可以使用__int____gt__方法进一步简化测试用例,但如果我删除其中任何一种方法,那么奇怪的行为就会消失.

Bre*_*arn 5

如评论中所述,dict已经定义了所有比较运算符.该记录的行为是:

除了不同的数字类型和不同的字符串类型之外,不同类型的对象永远不会相等; 这些对象是一致但任意的

换句话说,dicts被明确定义为允许与其他类型进行比较,但是这种比较的结果是不确定的.(这在Python 3中已更改,因此不再允许这些类型的类型间比较.)

当你只覆盖你的类型的一些比较运算符时,你会更加复杂化.由于您的类型定义__gt__但未定义__lt__,thing > 0.01将使用您的自定义__gt__,但thing < 0.01将使用默认(未定义)比较行为.因此,您获得的类型有时会使用确定性规则,有时会提供未定义的行为,具体取决于您使用的比较运算符.我不知道为什么你会看到你所看到的精确结果模式,但最重要的是你的类依赖于未定义的行为,所以你不能指望使用这种类型的比较有任何一致性.Python的两个实现可能在一些产生不同未定义行为的神秘实现级别上做了不同的事情.未定义行为的一点是你不应该知道它是如何工作的(或者你可能开始依赖它).

顺便说一下,total_ordering这是一个no-op,如果你删除它,行为应该是相同的. total_ordering只添加尚未定义的比较运算符,但dict已经定义了所有比较运算符,因此total_ordering不会执行任何操作.如果要在已定义其自己的比较行为(如dict)的类型的子类上创建自己的排序关系,则需要手动覆盖每个单独的比较运算符.


Aco*_*orn 1

经过进一步调查,并根据@BrenBarn 的精彩回答,我找到了奇怪行为的根源。

“未定义”比较的最后一步是比较对象类型的内存位置。在本地和 CI 服务器上进行比较后,我发现id(type(thing))本地的id 总是较高,而 CI 服务器上的 id 总是较低!id(type(0.02))Thing